<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>算法 &#8211; Blog of Code</title>
	<atom:link href="https://www.cztcode.com/category/heap/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.cztcode.com</link>
	<description></description>
	<lastBuildDate>Thu, 17 Feb 2022 02:43:14 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.cztcode.com/wp-content/uploads/2024/02/cropped-logo-32x32.webp</url>
	<title>算法 &#8211; Blog of Code</title>
	<link>https://www.cztcode.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">217219486</site>	<item>
		<title>垃圾邮件分类算法简介与测试</title>
		<link>https://www.cztcode.com/2022/4155/</link>
					<comments>https://www.cztcode.com/2022/4155/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Thu, 17 Feb 2022 02:43:14 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=4155</guid>

					<description><![CDATA[这篇文章将多项朴素贝叶斯，伯努利朴素贝叶斯，补充朴素贝叶斯，逻辑回归，支持向量机，KNN，决策树，随机森林，梯度提升，神经网络（多层感知机）算法用于垃圾邮件分类，测试比较不同算法的性能，选出适合作为垃圾邮件分类的算法。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">这篇文章将多项朴素贝叶斯，伯努利朴素贝叶斯，补充朴素贝叶斯，逻辑回归，支持向量机，KNN，决策树，随机森林，梯度提升，神经网络（多层感知机）算法用于垃圾邮件分类，测试比较不同算法的性能，选出适合作为垃圾邮件分类的算法。</p>



<h1 class="wp-block-heading">测试数据</h1>



<p class="is-style-iw-2em">使用了两个数据集分开测试的，UCI数据集有5574条数据，TREC有37822条数据。</p>



<p class="is-style-iw-2em">UCI数据集的数据是垃圾短信，TREC是垃圾邮件</p>



<h3 class="wp-block-heading">UCI相关数据集</h3>



<p class="is-style-iw-2em"><a href="https://archive.ics.uci.edu/ml/datasets/sms+spam+collection" target="_blank" rel="noopener">https://archive.ics.uci.edu/ml/datasets/sms+spam+collection</a></p>



<p class="is-style-iw-2em">邮件数目: 5572 垃圾邮件数目: 747 正常邮件数目: 4825 训练集大小: 4179 测试集大小: 1393</p>



<h3 class="wp-block-heading">2006 TREC Public Spam Corpora （trec06p）</h3>



<p class="is-style-iw-2em"><a href="https://plg.uwaterloo.ca/~gvcormac/treccorpus06/" target="_blank" rel="noopener">https://plg.uwaterloo.ca/~gvcormac/treccorpus06/</a></p>



<p class="is-style-iw-2em">邮件数目: 37822 垃圾邮件数目: 24912 正常邮件数目: 12910 训练集大小: 28366 测试集大小: 9456</p>



<h2 class="wp-block-heading">数据集处理</h2>



<p class="is-style-iw-2em">2006 TREC Public Spam Corpora 数据集需要经过处理转化后再进入下面的读入数据，过程看我的另一篇博客：</p>



<p class="is-style-iw-2em"><a href="https://www.cztcode.com/2022/trec-trec06p-dataset-processing/">https://www.cztcode.com/2022/trec-trec06p-dataset-processing/</a></p>



<h3 class="wp-block-heading">读入数据</h3>



<p class="is-style-iw-2em">将垃圾邮件spam和正常邮件ham用1，0 标记。</p>



<pre class="wp-block-preformatted">&nbsp;# 读取垃圾邮件数据<br>&nbsp;data_init = pd.read_table('SMSSpamCollection', sep='\t', names=['label', 'mem'])<br>&nbsp;​<br>&nbsp;# 数据预处理<br>&nbsp;data_init['label'] = data_init.label.map({'ham': 0, 'spam': 1}) &nbsp;# 0代表正常邮件，1代表垃圾邮件<br>&nbsp;total_count = data_init.shape[0]<br>&nbsp;spam_count = np.count_nonzero(data_init['label'].values) &nbsp;# 垃圾邮件数目<br>&nbsp;print("邮件数目:", total_count)<br>&nbsp;print("垃圾邮件数目:", spam_count)<br>&nbsp;print("正常邮件数目:", total_count - spam_count)</pre>



<h3 class="wp-block-heading">划分训练集和测试集</h3>



<p class="is-style-iw-2em">sklearn提供的train_test_split()函数可以将数据拆分为训练集和测试集</p>



<p class="is-style-iw-2em">random_state=1 表示每次运行得到相同结果（固定划分）</p>



<p class="is-style-iw-2em">stratify=data_init[&#8216;label&#8217;] 表示启用分层拆分，测试集里spam和ham的比例和训练集保持相同</p>



<pre class="wp-block-preformatted">&nbsp;x_train, x_test, y_train, y_test = train_test_split(data_init['mem'], data_init['label'], random_state=1,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;stratify=data_init['label'])<br>&nbsp;print('训练集大小: {}'.format(x_train.shape[0]))<br>&nbsp;print('测试集大小: {}'.format(x_test.shape[0]))</pre>



<h3 class="wp-block-heading">转化为稀疏矩阵</h3>



<h4 class="wp-block-heading">词袋模型</h4>



<p class="is-style-iw-2em">词袋模型能够把一个句子转化为向量表示，是比较简单直白的一种方法，它不考虑句子中单词的顺序，只考虑词表（vocabulary）中单词在这个句子中的出现次数。</p>



<p class="is-style-iw-2em">stop_words=&#8217;english&#8217; 表示使用内置英语停用词，比如a，the 这种冠词很高频但是对分析无效，所以就直接去掉。</p>



<pre class="wp-block-preformatted">&nbsp;count_vector = CountVectorizer(stop_words='english')<br>&nbsp;# 学习词汇词典并返回术语 - 文档矩阵(稀疏矩阵)。<br>&nbsp;train_data = count_vector.fit_transform(x_train)<br>&nbsp;# 使用符合fit的词汇表或提供给构造函数的词汇表，从原始文本文档中提取词频，转换成词频矩阵<br>&nbsp;test_data = count_vector.transform(x_test)</pre>



<p class="is-style-iw-2em">先使用fit_transform(x_train)根据停用词表构造出词频矩阵，再使用transform(x_test)把测试集按照已经构造出的词频矩阵的词顺序计算出测试集的词频矩阵。</p>



<h1 class="wp-block-heading">评估标准</h1>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/ffbe1563-4943-4702-976f-4f10e9076e2a.png" alt="ffbe1563-4943-4702-976f-4f10e9076e2a"/></figure>



<p class="is-style-iw-2em"><strong>查准率P（正确率（Precision））：</strong>垃圾邮件检对率。查准率越高“漏网”的垃圾邮件就越少。</p>



<p class="is-style-iw-2em"><strong>查全率R（召回率（Recall））：</strong>垃圾邮件检出率。这个指标反映了过滤系统发现垃圾邮件的能力，查全率反应了过滤系统“找对”垃圾邮件的能力，查全率越大将合法邮件误判为垃圾邮件的可能性越小。</p>



<p class="is-style-iw-2em">直观地说，<a href="https://en.wikipedia.org/wiki/Precision_and_recall#Precision" target="_blank" rel="noopener">精度</a>P是分类器不将负样本标记为正样本的能力，而 <a href="https://en.wikipedia.org/wiki/Precision_and_recall#Recall" target="_blank" rel="noopener">召回</a>R是分类器找到所有正样本的能力。</p>



<p class="is-style-iw-2em"><strong>精确率（Accuracy）</strong>：即对所有邮件（包括垃圾邮件和合法邮件）的判对率。Accuracy=(TP+TN)/N</p>



<p class="is-style-iw-2em"><strong>F1度量：</strong>F1实际上是召回率和正确率的调和平均它将召回率和正确率综合成一个指标。F1越大说明模型的效果越好。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220213145918791.png" alt="image-20220213145918791"/></figure>



<p class="is-style-iw-2em"><strong>TCR:</strong> 人们往往不希望将合法邮件误判成垃圾邮件。为了表示不同情况下垃圾邮件系统的代价Androutsopoulos 等人提出了代价因子的概念。假设将合法邮件误判为垃圾邮件的损失为是垃圾邮件判为合法邮件的 λ倍。比如λ＝ 9表示一封合法邮件误判的损失是一封垃圾邮件误判的9倍。</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2022/04/image.png" alt="" class="wp-image-4195"/></figure></div>



<p class="is-style-iw-2em">这个公式是化简得来的（MarkDown打个公式费多大劲QAQ），TCR 越高表明当前垃圾邮件过滤系统的损失越低。</p>



<p class="is-style-iw-2em">在测试中测试 λ取2。</p>



<h2 class="wp-block-heading">在sklearn中输出分类混淆矩阵并计算TCR</h2>



<p class="is-style-iw-2em">sklearn的分类混淆矩阵定义和上面图示的不一样，所以需要加一个label标签转化一下。</p>



<p class="is-style-iw-2em"><a href="https://blog.csdn.net/weixin_34809240/article/details/114439832" target="_blank" rel="noopener">https://blog.csdn.net/weixin_34809240/article/details/114439832</a></p>



<pre class="wp-block-code"><code>&nbsp; &nbsp; &nbsp;y_true = &#91;0, 1, 0, 1, 0, 1, 0];<br>&nbsp; &nbsp; &nbsp;y_pred = &#91;1, 1, 1, 0, 1, 0, 1];<br>&nbsp; &nbsp; &nbsp;cm = confusion_matrix(y_true, y_pred, labels=&#91;1, 0]);<br>&nbsp; &nbsp; &nbsp;TP = cm&#91;0]&#91;0]<br>&nbsp; &nbsp; &nbsp;FP = cm&#91;1]&#91;0]<br>&nbsp; &nbsp; &nbsp;TN = cm&#91;1]&#91;1]   <br>&nbsp; &nbsp; &nbsp;FN = cm&#91;0]&#91;1]<br>&nbsp; &nbsp; &nbsp;print("TP:", FP)<br>&nbsp; &nbsp; &nbsp;print("FP:", FP)<br>&nbsp; &nbsp; &nbsp;print("TN:", TN)<br>&nbsp; &nbsp; &nbsp;print("FN:", FN)<br>&nbsp;输出结果：<br>&nbsp;    TP: 4<br>&nbsp;    FP: 4<br>&nbsp;    TN: 0<br>&nbsp;    FN: 2</code></pre>



<p class="is-style-iw-2em">这里我写了一个计算TCR的函数，k取2</p>



<pre class="wp-block-code"><code>&nbsp;def tcr_score(y_true, y_pred):<br>&nbsp; &nbsp; &nbsp;cm = confusion_matrix(y_true, y_pred, labels=&#91;1, 0]);<br>&nbsp; &nbsp; &nbsp;tp = cm&#91;0]&#91;0]<br>&nbsp; &nbsp; &nbsp;fp = cm&#91;1]&#91;0]<br>&nbsp; &nbsp; &nbsp;fn = cm&#91;0]&#91;1]<br>&nbsp; &nbsp; &nbsp;tcr = (tp + fp) / (K * fn + fp)<br>&nbsp; &nbsp; &nbsp;return tcr</code></pre>



<h2 class="wp-block-heading">比较标准</h2>



<p class="is-style-iw-2em">根据TCR越高的算法分类效果越好，<strong>最后按照TCR分值排名</strong>。</p>



<h1 class="wp-block-heading">朴素贝叶斯</h1>



<p class="is-style-iw-2em">关于朴素贝叶斯原理我写过另一篇博客：<a href="https://www.cztcode.com/2022/machine-learning-chapter-7/">https://www.cztcode.com/2022/machine-learning-chapter-7/</a></p>



<h2 class="wp-block-heading"><strong>伯努利、多项式和高斯朴素贝叶斯之间的区别</strong></h2>



<ul class="wp-block-list"><li>MultinomialNB使用出现次数（<code>频数</code>）</li><li>BernoulliNB设计用于<code>二进制/布尔特征</code></li><li>GaussianNB用于连续的数据。例如日温度，高度。</li></ul>



<p class="is-style-iw-2em">高斯分布不适合文本分类，这里就不讨论了。</p>



<h3 class="wp-block-heading">多项式模型：</h3>



<p class="is-style-iw-2em">设某文档d=(t1,t2,…,tk)，tk是该文档中出现过的单词，允许重复，则</p>



<p class="is-style-iw-2em">(1)<strong>先验概率</strong>P(c)= 类c下样本总数/整个训练样本的样本总数</p>



<p class="is-style-iw-2em">(2)<strong>类条件概率</strong>P(tk|c) =(类c下单词tk数目+α)/(指定类下所有特征出现次数之和+类别数*α)</p>



<h3 class="wp-block-heading">伯努利模型：</h3>



<p class="is-style-iw-2em">(1)<strong>先验概率</strong>P(c)= 类c下样本总数/整个训练样本的样本总数</p>



<p class="is-style-iw-2em">(2)<strong>类条件概率</strong>P(tk|c)=(类c下包含单词tk的文件数+α)/(类c下样本数+类别数*α)</p>



<h3 class="wp-block-heading">多项式模型和伯努利模型比较</h3>



<ol class="wp-block-list"><li>贝努利模型不考虑<strong>词项出现的次数</strong>，而多项式模型考虑</li><li>贝努利模型适合处理<strong>短文档</strong>，而多项式模型适合处理<strong>长文档</strong></li><li>贝努利模型在<strong>特征数较少</strong>时效果更好，而多项式模型在<strong>特征较多</strong>时效果更好</li><li>多项式模型：充分考虑了词频的影响，显然的，一篇文章中一个词的词频越高就越有代表性，因此应该考虑词频的影响。 但是，这样的话却避免不了极端数据的影响，比如行中有一行出现100个study，而其他很多行都没有出现study的情况 伯努利模型：实现上比较容易，能降低上述的极端数据的影响</li></ol>



<h2 class="wp-block-heading">多项式模型朴素贝叶斯分类器</h2>



<h3 class="wp-block-heading">测试结果</h3>



<p class="is-style-iw-2em">参数简介：</p>



<p class="is-style-iw-2em"><strong>alpha*</strong>float, default=1.0*</p>



<p class="is-style-iw-2em">Additive (Laplace/Lidstone) smoothing parameter (0 for no smoothing).</p>



<p class="is-style-iw-2em"><strong>fit_prior*</strong>bool, default=True*</p>



<p class="is-style-iw-2em">Whether to learn class prior probabilities or not. If false, a uniform prior will be used.</p>



<p class="is-style-iw-2em"><strong>class_prior*</strong>array-like of shape (n_classes,), default=None*</p>



<p class="is-style-iw-2em">Prior probabilities of the classes. If specified the priors are not adjusted according to the data.</p>



<p class="is-style-iw-2em">这里采用了拉普拉斯修正，懒惰学习模式（介绍看上面博客）</p>



<pre class="wp-block-code"><code>&nbsp;# 使用多项朴素贝叶斯模型对数据进行拟合<br>&nbsp;naive_bayes = MultinomialNB(alpha=1.0, class_prior=None, fit_prior=True)<br>&nbsp;naive_bayes.fit(train_data, y_train)<br>&nbsp;predictions_nb = naive_bayes.predict(test_data)</code></pre>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">naive bayes Accuracy score: 0.9870782483847811 naive bayes Precision score: 0.9668508287292817 naive bayes Recall score: 0.9358288770053476 naive bayes F1 score: 0.951086956521739 naive bayes TCR score: 6.033333333333333</p>



<h4 class="wp-block-heading">TREC测试集结果</h4>



<p class="is-style-iw-2em">naive bayes Accuracy score: 0.9685913705583756 naive bayes Precision score: 0.9920358387257342 naive bayes Recall score: 0.960019267822736 naive bayes F1 score: 0.9757649938800491 naive bayes TCR score: 11.038461538461538</p>



<h2 class="wp-block-heading">伯努利朴素贝叶斯分类器</h2>



<h3 class="wp-block-heading">测试结果</h3>



<pre class="wp-block-preformatted">&nbsp;# 伯努利朴素贝叶斯模型数据拟合<br>&nbsp;b_naive_bayes = BernoulliNB(alpha=1.0, class_prior=None, fit_prior=True)<br>&nbsp;b_naive_bayes.fit(train_data, y_train)<br>&nbsp;b_predictions_nb = b_naive_bayes.predict(test_data)</pre>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">Bernoulli bayes Accuracy score: 0.9712849964106246 Bernoulli bayes Precision score: 1.0 Bernoulli bayes Recall score: 0.786096256684492 Bernoulli bayes F1 score: 0.8802395209580839 Bernoulli bayes TCR score: 1.8375</p>



<h4 class="wp-block-heading">TREC 测试集结果</h4>



<p class="is-style-iw-2em">Bernoulli bayes Accuracy score: 0.9065143824027073 Bernoulli bayes Precision score: 0.881061038220194 Bernoulli bayes Recall score: 0.9919717405266538 Bernoulli bayes F1 score: 0.9332326283987916 Bernoulli bayes TCR score: 7.507494646680942</p>



<h2 class="wp-block-heading">补充朴素贝叶斯分类器</h2>



<p class="is-style-iw-2em">sklearn还提供优化的朴素贝叶斯分类器，一同测试一下。</p>



<p class="is-style-iw-2em">CNB是标准多项式朴素贝叶斯(MNB)算法的一种自适应算法，特别适用于不平衡的数据集。具体而言，CNB使用来自每个类的补充的统计数据来计算模型的权重。CNB的发明者经验性地表明，CNB的参数估计比MNB的参数估计更稳定。此外，CNB在文本分类任务方面经常优于MNB(通常以相当大的幅度)。</p>



<pre class="wp-block-preformatted">&nbsp;# 使用补充朴素贝叶斯模型对数据进行拟合<br>&nbsp;c_naive_bayes = ComplementNB(alpha=1.0, class_prior=None, fit_prior=True)<br>&nbsp;c_naive_bayes.fit(train_data, y_train)<br>&nbsp;c_predictions_nb = c_naive_bayes.predict(test_data)</pre>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">complement bayes Accuracy score: 0.9712849964106246 complement bayes Precision score: 0.8483412322274881 complement bayes Recall score: 0.9572192513368984 complement bayes F1 score: 0.899497487437186 complement bayes TCR score: 4.395833333333333</p>



<h4 class="wp-block-heading">TREC 测试集结果</h4>



<p class="is-style-iw-2em">complement bayes Accuracy score: 0.9588620981387479 complement bayes Precision score: 0.9979532662459492 complement bayes Recall score: 0.9394669235709698 complement bayes F1 score: 0.967827309569101 complement bayes TCR score: 7.654046997389034</p>



<h2 class="wp-block-heading">朴素贝叶斯分类总结</h2>



<p class="is-style-iw-2em">按照TCR排名，最好的是多项式模型朴素贝叶斯分类器，其次是补充朴素贝叶斯分类器，最差的是伯努利朴素贝叶斯分类器。当使用样本数更多的TREC数据集后，伯努利朴素贝叶斯和补充朴素贝叶斯的分类效果接近，数据集的增加对伯努利朴素贝叶斯分类效果提升明显。</p>



<p class="is-style-iw-2em">补充朴素贝叶斯分类器效果比多项式模型差。</p>



<h1 class="wp-block-heading">KNN算法</h1>



<p class="is-style-iw-2em">一文搞懂k近邻（k-NN）算法（一） &#8211; 忆臻的文章 &#8211; 知乎 <a href="https://zhuanlan.zhihu.com/p/25994179" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/25994179</a></p>



<p class="is-style-iw-2em">KNN算法（k近邻算法）：k近邻算法是一种<strong>基本分类和回归方法</strong>。给定一个训练数据集，对新的输入实例，在训练数据集中找到与该实例<strong>最邻近</strong>的K个实例，<strong>这K个实例的多数属于某个类</strong>，就把该输入实例分类到这个类中。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-c3f1d2553e7467d7da5f9cd538d2b49a_720w.png" alt="img"/></figure>



<p class="is-style-iw-2em">如上图所示，有<strong>两类</strong>不同的样本数据，分别用蓝色的小正方形和红色的小三角形表示，而图正中间的那个绿色的圆所标示的数据则是<strong>待分类的数据</strong>。这也就是我们的目的，来了一个新的数据点，我要得到它的类别是什么？好的，下面我们根据k近邻的思想来给绿色圆点进行分类。</p>



<ul class="wp-block-list"><li>如果K=3，绿色圆点的最邻近的3个点是2个红色小三角形和1个蓝色小正方形，<strong>少数从属于多数，</strong>基于统计的方法，判定绿色的这个待分类点属于红色的三角形一类。</li><li>如果K=5，绿色圆点的最邻近的5个邻居是2个红色三角形和3个蓝色的正方形，<strong>还是少数从属于多数，</strong>基于统计的方法，判定绿色的这个待分类点属于蓝色的正方形一类。</li></ul>



<h2 class="wp-block-heading">K值的选取</h2>



<p class="is-style-iw-2em"><strong>选取较小的k值会意味着我们的整体模型会变得复杂，容易发生过拟合</strong></p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-6911dd1ce577c9fd6842cbd2ee68a309_720w.png" alt="img"/></figure>



<p class="is-style-iw-2em">比如这种情况K等于1时会分类到黑色原点，也就是学习到了噪声，过拟合。</p>



<p class="is-style-iw-2em">所谓的过拟合就是在训练集上准确率非常高，而在测试集上准确率低，经过上例，我们可以得到k太小会导致<strong>过拟合</strong>，<strong>很容易将一些噪声（如上图离五边形很近的黑色圆点）学习到模型中，而忽略了数据真实的分布！</strong></p>



<p class="is-style-iw-2em"><strong>如果我们选取较大的k值，就相当于用较大邻域中的训练数据进行预测，这时与输入实例较远的（不相似）训练实例也会对预测起作用，使预测发生错误，k值的增大意味着整体模型变得简单。</strong></p>



<p class="is-style-iw-2em">所以K值在下面红色园边界之内是最好的</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-b7dc18ee84e5c099c21fbaa175a7b9c6_720w.png" alt="img"/></figure>



<h2 class="wp-block-heading">距离的度量</h2>



<p class="is-style-iw-2em">k近邻算法是在训练数据集中找到与该实例<strong>最邻近</strong>的K个实例，这K个实例的多数属于某个类，我们就说预测点属于哪个类。</p>



<p class="is-style-iw-2em">定义中所说的最邻近是如何度量呢？我们怎么知道谁跟测试点最邻近。这里就会引出我们几种度量俩个点之间距离的标准。</p>



<p class="is-style-iw-2em">我们可以有以下几种度量方式：</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-60bb382b0d22ec0ce296ed0e024f31bc_720w.png" alt="img"/></figure>



<p class="is-style-iw-2em">其中当p=2的时候，就是我们最常见的欧式距离，我们也一般都用欧式距离来衡量我们高维空间中俩点的距离。在实际应用中，距离函数的选择应该根据数据的特性和分析的需要而定，一般选取p=2欧式距离表示，这不是本文的重点。</p>



<p class="is-style-iw-2em"><strong>恩，距离度量我们也了解了，下面我要说一下各个维度归一化的必要性！</strong></p>



<p class="is-style-iw-2em"><strong>3.特征归一化的必要性</strong></p>



<p class="is-style-iw-2em">首先举例如下，我用一个人身高(cm)与脚码（尺码）大小来作为特征值，类别为男性或者女性。我们现在如果有5个训练样本，分布如下：</p>



<p class="is-style-iw-2em">A [(179,42),男] B [(178,43),男] C [(165,36)女] D [(177,42),男] E [(160,35),女]</p>



<p class="is-style-iw-2em">通过上述训练样本，我们看出问题了吗？</p>



<p class="is-style-iw-2em">很容易看到第一维身高特征是第二维脚码特征的4倍左右，那么在进行距离度量的时候，<strong>我们就会偏向于第一维特征。</strong>这样造成俩个特征并不是等价重要的，最终可能会导致距离计算错误，从而导致预测错误。口说无凭，举例如下：</p>



<p class="is-style-iw-2em">现在我来了一个测试样本 F(167,43)，让我们来预测他是男性还是女性，我们采取k=3来预测。</p>



<p class="is-style-iw-2em">下面我们用欧式距离分别算出F离训练样本的欧式距离，然后选取最近的3个，多数类别就是我们最终的结果，计算如下：</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-07d94c435dc95d66091768d56499f363_720w.png" alt="img"/></figure>



<p class="is-style-iw-2em">由计算可以得到，最近的前三个分别是C,D,E三个样本，那么由C,E为女性，D为男性，女性多于男性得到我们要预测的结果为<strong>女性</strong>。</p>



<p class="is-style-iw-2em"><strong>这样问题就来了，一个女性的脚43码的可能性，远远小于男性脚43码的可能性，那么为什么算法还是会预测F为女性呢？那是因为由于各个特征量纲的不同，在这里导致了身高的重要性已经远远大于脚码了，这是不客观的。</strong>所以我们应该让每个特征都是同等重要的！这也是我们要归一化的原因！归一化公式如下：</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-be30691d37ac93b2237217cadca2e967_720w.png" alt="img"/></figure>



<h2 class="wp-block-heading">测试结果</h2>



<p class="is-style-iw-2em">用sklearn提供的knn分类器进行分类。这里的k取1，我试过了k取的越大，F1的值越低，所以最好的结果就是n_neighbors取1。</p>



<pre class="wp-block-preformatted">&nbsp;# KNN算法<br>&nbsp;k_neighbor = KNeighborsClassifier(n_neighbors=1, weights='uniform')<br>&nbsp;k_neighbor.fit(train_data, y_train)<br>&nbsp;predictions_knn = k_neighbor.predict(test_data)</pre>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">knn Accuracy score: 0.9533381191672649 knn Precision score: 1.0 knn Recall score: 0.6524064171122995 knn F1 score: 0.7896440129449839 knn TCR score: 0.9384615384615385</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220213143732174.png" alt="image-20220213143732174"/></figure>



<p class="is-style-iw-2em"><del>我没看出来KNN和朴素贝叶斯比性能可以相当。。。 k取较小值性能较好是真的。</del></p>



<p class="is-style-iw-2em"><del><strong>结论</strong>： KNN不太适合用于垃圾邮件分类，召回率太低会导致垃圾邮件检出率低，F1得分和TCR得分都很低。</del></p>



<h4 class="wp-block-heading">TREC 测试集结果</h4>



<p class="is-style-iw-2em">使用trec06p更大的数据集后，knn的效果果然和朴素贝叶斯相当了</p>



<p class="is-style-iw-2em">邮件数目: 37822 垃圾邮件数目: 24912 正常邮件数目: 12910 邮件正文缺失数目： 0 训练集大小: 28366 测试集大小: 9456 knn Accuracy score: 0.9588620981387479 knn Precision score: 0.9712671509281678 knn Recall score: 0.9661207450224791 knn F1 score: 0.9686871126137003 knn TCR score: 10.325</p>



<p class="is-style-iw-2em">多项朴素贝叶斯的成绩</p>



<p class="is-style-iw-2em">naive bayes Accuracy score: 0.9685913705583756 naive bayes Precision score: 0.9920358387257342 naive bayes Recall score: 0.960019267822736 naive bayes F1 score: 0.9757649938800491 naive bayes TCR score: 11.038461538461538</p>



<p class="is-style-iw-2em"><strong>真的相差无几</strong></p>



<p class="is-style-iw-2em"><strong>结论：</strong>：knn在样本集更大时和朴素贝叶斯有相近的性能，但是knn分类的速度要慢于朴素贝叶斯。</p>



<h1 class="wp-block-heading">SVM</h1>



<p class="is-style-iw-2em">支持向量机（support vector machines, SVM）是一种二分类模型，它的基本模型是定义在特征空间上的<strong>间隔最大的线性分类器</strong>，间隔最大使它有别于感知机；SVM还包括<strong>核技巧</strong>，这使它成为实质上的非线性分类器。SVM的的学习策略就是间隔最大化，可形式化为一个求解凸二次规划的问题，也等价于正则化的合页损失函数的最小化问题。SVM的的学习算法就是求解凸二次规划的最优化算法。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-f9e1e7fd08460a5fab044c71ed8b0bb1_720w.jpg" alt="img"/></figure>



<p class="is-style-iw-2em"><strong>在决定最佳超平面时只有支持向量起作用，而其他数据点并不起作用</strong>。如果移动非支持向量，甚至删除非支持向量都不会对最优超平面产生任何影响。也即支持向量对模型起着决定性的作用，这也是“支持向量机”名称的由来。</p>



<h3 class="wp-block-heading">SVM算法的主要优点有：</h3>



<ul class="wp-block-list"><li>解决高维特征的分类问题和回归问题很有效,在特征维度大于样本数时依然有很好的效果。</li><li>仅仅使用一部分支持向量来做超平面的决策，无需依赖全部数据。</li><li>有大量的核函数可以使用，从而可以很灵活的来解决各种非线性的分类回归问题。</li><li>样本量不是海量数据的时候，分类准确率高，泛化能力强。</li></ul>



<h3 class="wp-block-heading">SVM算法的主要缺点有：</h3>



<ul class="wp-block-list"><li>如果特征维度远远大于样本数，则SVM表现一般。</li><li>SVM在样本量非常大，核函数映射维度非常高时，计算量过大，不太适合使用。</li><li>非线性问题的核函数的选择没有通用标准，难以选择一个合适的核函数。</li><li>SVM对缺失数据敏感。</li></ul>



<h2 class="wp-block-heading">测试结果</h2>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">support vector machine Accuracy score: 0.9806173725771715 support vector machine Precision score: 1.0 support vector machine Recall score: 0.8556149732620321 support vector machine F1 score: 0.9221902017291067 support vector machine TCR score: 2.962962962962963</p>



<h4 class="wp-block-heading">TREC 测试集结果</h4>



<p class="is-style-iw-2em">support vector machine Accuracy score: 0.8212774957698815 support vector machine Precision score: 0.7880904012188928 support vector machine Recall score: 0.9966281310211946 support vector machine F1 score: 0.8801758366420874 support vector machine TCR score: 4.603156049094097</p>



<h2 class="wp-block-heading">LinearSVC</h2>



<pre class="wp-block-preformatted">&nbsp;svm_clf = svm.LinearSVC()<br>&nbsp;svm_clf.fit(train_data, y_train)<br>&nbsp;predictions_svm = svm_clf.predict(test_data)</pre>



<h2 class="wp-block-heading">测试结果</h2>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">support vector machine Accuracy score: 0.9820531227566404 support vector machine Precision score: 0.9939024390243902 support vector machine Recall score: 0.8716577540106952 support vector machine F1 score: 0.9287749287749287 support vector machine TCR score: 3.3469387755102042</p>



<h3 class="wp-block-heading">TREC 测试集结果</h3>



<p class="is-style-iw-2em">support vector machine Accuracy score: 0.9845600676818951 support vector machine Precision score: 0.9814756174794174 support vector machine Recall score: 0.9953436095054592 support vector machine F1 score: 0.9883609693877551 support vector machine TCR score: 36.09142857142857</p>



<p class="is-style-iw-2em"><strong>结论</strong>：在垃圾邮件过滤任务中，大量实验表明线性核表现出很高的性能，与其他核函数的性能相近。而对线性核进行合适的特征表示可获取较佳的分类性能和较低的计算复杂度。</p>



<h1 class="wp-block-heading">逻辑回归</h1>



<p class="is-style-iw-2em">逻辑回归介绍：<a href="https://zhuanlan.zhihu.com/p/74874291" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/74874291</a></p>



<h2 class="wp-block-heading">测试结果</h2>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">logistic regression Accuracy score: 0.9791816223977028 logistic regression Precision score: 1.0 logistic regression Recall score: 0.8449197860962567 logistic regression F1 score: 0.9159420289855073 logistic regression TCR score: 2.7241379310344827</p>



<h4 class="wp-block-heading">TREC 测试集结果</h4>



<p class="is-style-iw-2em">logistic regression Accuracy score: 0.9849830795262268 logistic regression Precision score: 0.9811827956989247 logistic regression Recall score: 0.9963070006422607 logistic regression F1 score: 0.9886870618228171 logistic regression TCR score: 38.32727272727273</p>



<h1 class="wp-block-heading">决策树</h1>



<p class="is-style-iw-2em">决策树是一种逻辑简单的机器学习算法，它是一种树形结构，所以叫决策树。</p>



<p class="is-style-iw-2em">决策树简介：<a href="https://easyai.tech/ai-definition/decision-tree/" target="_blank" rel="noopener">https://easyai.tech/ai-definition/decision-tree/</a></p>



<pre class="wp-block-preformatted">&nbsp;decision_tree = DecisionTreeClassifier()<br>&nbsp;decision_tree.fit(train_data, y_train)<br>&nbsp;predictions_dt = decision_tree.predict(test_data)</pre>



<h2 class="wp-block-heading">测试结果</h2>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">decision tree Accuracy score: 0.9698492462311558 decision tree Precision score: 0.9190751445086706 decision tree Recall score: 0.8502673796791443 decision tree F1 score: 0.8833333333333333 decision tree TCR score: 2.4714285714285715</p>



<h4 class="wp-block-heading">TREC 测试集结果</h4>



<p class="is-style-iw-2em">decision tree Accuracy score: 0.9755710659898477 decision tree Precision score: 0.9731734259113145 decision tree Recall score: 0.9902055234425177 decision tree F1 score: 0.981615598885794 decision tree TCR score: 21.70205479452055</p>



<h1 class="wp-block-heading">随机森林</h1>



<p class="is-style-iw-2em">随机森林是由很多决策树构成的，不同决策树之间没有关联。当我们进行分类任务时，新的输入样本进入，就让森林中的每一棵决策树分别进行判断和分类，每个决策树会得到一个自己的分类结果，决策树的分类结果中哪一个分类最多，那么随机森林就会把这个结果当做最终的结果。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/2019-08-21-Random-Forest.png" alt="图解随机森林"/></figure>



<p class="is-style-iw-2em">随机森林介绍：<a href="https://easyai.tech/ai-definition/random-forest/" target="_blank" rel="noopener">https://easyai.tech/ai-definition/random-forest/</a></p>



<h2 class="wp-block-heading">测试结果</h2>



<pre class="wp-block-preformatted">&nbsp;random_forest = RandomForestClassifier()<br>&nbsp;random_forest.fit(train_data, y_train)<br>&nbsp;predictions_rf = random_forest.predict(test_data)</pre>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">random forest Accuracy score: 0.9755922469490309 random forest Precision score: 1.0 random forest Recall score: 0.8181818181818182 random forest F1 score: 0.9 random forest TCR score: 2.25</p>



<h4 class="wp-block-heading">TREC 测试集结果</h4>



<p class="is-style-iw-2em">random forest Accuracy score: 0.9865693739424704 random forest Precision score: 0.9895682875942866 random forest Recall score: 0.9900449582530507 random forest F1 score: 0.9898065655349547 random forest TCR score: 32.96825396825397</p>



<h1 class="wp-block-heading"><strong>Gradient Boosting</strong></h1>



<pre class="wp-block-preformatted">&nbsp;gdbt = GradientBoostingClassifier()<br>&nbsp;gdbt.fit(train_data, y_train)<br>&nbsp;predictions_gdbt = gdbt.predict(test_data)</pre>



<h2 class="wp-block-heading">测试结果</h2>



<p class="is-style-iw-2em">Gradient Boosting：<a href="https://zhuanlan.zhihu.com/p/26327929" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/26327929</a></p>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">gradient boosting Accuracy score: 0.9619526202440776 gradient boosting Precision score: 0.9926470588235294 gradient boosting Recall score: 0.7219251336898396 gradient boosting F1 score: 0.8359133126934984 gradient boosting TCR score: 1.2952380952380953</p>



<h4 class="wp-block-heading">TREC 测试集结果</h4>



<p class="is-style-iw-2em">gradient boosting Accuracy score: 0.9526226734348562 gradient boosting Precision score: 0.9376135675348274 gradient boosting Recall score: 0.9942196531791907 gradient boosting F1 score: 0.9650872817955112 gradient boosting TCR score: 13.644628099173554</p>



<h1 class="wp-block-heading">神经网络</h1>



<p class="is-style-iw-2em">多层感知器的优点包括：</p>



<ul class="wp-block-list"><li>学习非线性模型的能力。</li><li>实时学习模型的能力（在线学习）<code>partial_fit</code>。</li></ul>



<p class="is-style-iw-2em">多层感知器的缺点包括：</p>



<ul class="wp-block-list"><li>具有隐藏层的MLP非凸性损失函数，其中存在多个局部最小值。因此，不同的随机权重初始化可能导致不同的精度。</li><li>MLP需要调整许多超参数，例如隐藏神经元的数量，层数和迭代次数。</li><li>MLP对特征缩放很敏感。</li></ul>



<p class="is-style-iw-2em">[ 分享 ] Sklearn 中的神经网络 Neural network models &#8211; napher的文章 &#8211; 知乎 <a href="https://zhuanlan.zhihu.com/p/352330001" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/352330001</a></p>



<p class="is-style-iw-2em">从线性回归到逻辑回归再到人工神经网络：<a href="http://lijinglin.cn/index.php/archives/21.html" target="_blank" rel="noopener">http://lijinglin.cn/index.php/archives/21.html</a></p>



<p class="is-style-iw-2em"><strong>求解器*</strong>{&#8216;lbfgs&#8217;，&#8217;sgd&#8217;，&#8217;adam&#8217;}，默认 =&#8217;adam&#8217;*</p>



<p class="is-style-iw-2em">权重优化的求解器。</p>



<ul class="wp-block-list"><li>&#8216;lbfgs&#8217; 是准牛顿方法家族中的优化器。</li><li>&#8216;sgd&#8217; 指的是随机梯度下降。</li><li>“adam”指的是由 Kingma、Diederik 和 Jimmy Ba 提出的基于随机梯度的优化器</li></ul>



<p class="is-style-iw-2em">注意：就训练时间和验证分数而言，默认求解器“adam”在相对较大的数据集（具有数千个训练样本或更多）上运行良好。然而，对于小型数据集，“lbfgs”可以更快地收敛并表现更好。</p>



<p class="is-style-iw-2em">UCI测试集使用lbfgs，TREC测试集使用adam</p>



<pre class="wp-block-preformatted">&nbsp;mlp = MLPClassifier(solver='lbfgs', activation='logistic')<br>&nbsp;mlp.fit(train_data, y_train)<br>&nbsp;predictions_nn = mlp.predict(test_data)</pre>



<h2 class="wp-block-heading">测试结果</h2>



<h4 class="wp-block-heading">UCI 测试集结果</h4>



<p class="is-style-iw-2em">neural network Accuracy score: 0.9863603732950467 neural network Precision score: 0.9772727272727273 neural network Recall score: 0.9197860962566845 neural network F1 score: 0.9476584022038568 neural network TCR score: 5.176470588235294</p>



<h4 class="wp-block-heading">TREC 测试集结果</h4>



<p class="is-style-iw-2em">neural network Accuracy score: 0.9884729272419628 neural network Precision score: 0.9869489097564857 neural network Recall score: 0.9956647398843931 neural network F1 score: 0.9912876668531693 neural network TCR score: 46.1985294117647</p>



<h1 class="wp-block-heading">总结</h1>



<h2 class="wp-block-heading">TCR分数</h2>



<figure class="wp-block-table"><table><thead><tr><th>算法</th><th>naive bayes</th><th>complement bayes</th><th>Bernoulli bayes</th><th>logistic regression</th><th>support vector machine</th><th>knn</th><th>decision tree</th><th>random forest</th><th>gradient boosting TCR score</th><th>neural network TCR score</th></tr></thead><tbody><tr><td>SPEC TCR 得分</td><td>11.03</td><td>7.65</td><td>7.5</td><td>38.32</td><td>36.09</td><td>10</td><td>21.91</td><td>33.17</td><td>13.64</td><td>46.19</td></tr><tr><td>UCI TCR得分</td><td>6.03</td><td>4.39</td><td>1.83</td><td>2.72</td><td>3.34</td><td>0.9</td><td>2.47</td><td>2.42</td><td>1.25</td><td>4.09</td></tr></tbody></table></figure>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/%E5%9E%83%E5%9C%BE%E9%82%AE%E4%BB%B6.png" alt="垃圾邮件"/></figure>



<p class="is-style-iw-2em">可以看出神经网络在数据集更大时有更好的分类效果，当数据集较小时，多项朴素贝叶斯有着更快的分类速度和更好的分类结果。</p>



<p class="is-style-iw-2em">使用神经网络进行分类还可以对模型进行增量更新，适合对一定样本进行训练后再通过用户手动标记垃圾邮件获取更精确的分类结果。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2022/4155/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4155</post-id>	</item>
		<item>
		<title>TREC（trec06p）数据集处理</title>
		<link>https://www.cztcode.com/2022/4153/</link>
					<comments>https://www.cztcode.com/2022/4153/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Thu, 17 Feb 2022 02:39:38 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=4153</guid>

					<description><![CDATA[2006 TREC Public Spam Corpora （trec06p） https://plg.uwaterloo.ca/~gvcormac/treccorpus06/ TREC的数据集是按照文件提供的，每个邮件在一个文件中，通过一个index索引标记spam和ham。下面这段代码提取出邮件正文，并将所有邮件的正文和标记输出到一个文件中，便于下一步处理转化词袋模型。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<h3 class="wp-block-heading">2006 TREC Public Spam Corpora （trec06p）</h3>



<p class="is-style-iw-2em"><a href="https://plg.uwaterloo.ca/~gvcormac/treccorpus06/" target="_blank" rel="noopener">https://plg.uwaterloo.ca/~gvcormac/treccorpus06/</a></p>



<p class="is-style-iw-2em">TREC的数据集是按照文件提供的，每个邮件在一个文件中，通过一个index索引标记spam和ham。下面这段代码提取出邮件正文，并将所有邮件的正文和标记输出到一个文件中，便于下一步处理转化词袋模型。</p>



<pre class="wp-block-code"><code>from email.parser import Parser

filetype="utf-8"
# 解析邮件内容
def get_body(msg):
    if msg.is_multipart():
        return get_body(msg.get_payload(0))
    else:
        return msg.get_payload(None, decode=True)


if __name__ == '__main__':
    f = open("E:/毕设/毕设数据/trec06p/full/index.txt", encoding=filetype)
    f1 = open("data.txt", "a+", encoding=filetype, errors='ignore')
    line = f.readline()
    num = 0
    while line:
        line = line.rstrip()
        temp = line.split(' ', 1)  # 以空格为分隔符，分隔成两个
        print(num / 37822 * 100)
        num += 1
        print("目前文件数:", num)
        path = "E:\\毕设\\毕设数据\\trec06p" + temp&#91;1].lstrip('..').replace('/', "\\")
        with open(path, encoding=filetype, errors='ignore') as f2:
            text = f2.read()
        f2.close()
        email = Parser().parsestr(text)
        text=get_body(email).decode(filetype, errors='ignore').replace('\n', '').replace('\r', '').replace('\t', '').strip()
        text=temp&#91;0]+'\t'+text+'\n'
        f1.write(text)
        print(text)
        line = f.readline()
    f.close()
    f1.close()
    print("处理完成")
</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2022/4153/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4153</post-id>	</item>
		<item>
		<title>机器学习第七章</title>
		<link>https://www.cztcode.com/2022/4149/</link>
					<comments>https://www.cztcode.com/2022/4149/#comments</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Thu, 10 Feb 2022 07:16:20 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=4149</guid>

					<description><![CDATA[主要是朴素贝叶斯相关，EM算法没看懂。。。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">主要是朴素贝叶斯相关，EM算法没看懂。。。</p>



<h2 class="wp-block-heading">贝叶斯判定准则</h2>



<p class="is-style-iw-2em">为最小化总体风险，只需在每个样本上选择能使风险最小的类别标记</p>



<h1 class="wp-block-heading">贝叶斯定理的名词解释</h1>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220210141031534.png" alt="image-20220210141031534"/></figure>



<p class="is-style-iw-2em">条件概率P(X|C)表示样本x在分类c下出现的概率，但很多样本在训练集根本没有出现，所以不能直接用频率来估计概率。</p>



<h1 class="wp-block-heading">朴素贝叶斯分类器</h1>



<p class="is-style-iw-2em">条件概率P(X|C)难以从有限的训练样本中估计，因为在某个分类下出现x是所有属性的联合概率，为了避开这个朴素贝叶斯分类器采用&#8221;属性条件独立性假设&#8221; ，就是假设这些属性是相互独立的。</p>



<p class="is-style-iw-2em">PS: <strong>朴素贝叶斯就朴素在假设这些属性相互独立</strong></p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220210145231761.png" alt="image-20220210145231761"/></figure>



<h3 class="wp-block-heading">拉普拉斯修正</h3>



<p class="is-style-iw-2em">如果出现了某个属性在训练集中没有出现的情况该如何处理呢，因为上面式子连乘时遇到某个条件概率为0时就会直接导致后验概率为0。</p>



<p class="is-style-iw-2em">表示训练集D中可能的类别 N; Ni表示第i个属性可能的取值数</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220210150051784.png" alt="image-20220210150051784"/></figure>



<h2 class="wp-block-heading">贝叶斯分类器的懒惰学习和增量学习</h2>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220210150215739.png" alt="image-20220210150215739"/></figure>



<p class="is-style-iw-2em"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2022/4149/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4149</post-id>	</item>
		<item>
		<title>机器学习第五章</title>
		<link>https://www.cztcode.com/2022/4147/</link>
					<comments>https://www.cztcode.com/2022/4147/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Wed, 09 Feb 2022 07:29:18 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=4147</guid>

					<description><![CDATA[神经元接收到来自其他神经元传递过来的输入信号，这些输入信号通过带权重的连接(connection) 进行传递 ，神经 接收到的总输入值将与神经元的阀值进行比较，，然后通过"激活函数" (activation function 处理以产生神经元的输出。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">中间3，4 章略读过去的，先不写博客。</p>



<h2 class="wp-block-heading">神经元模型</h2>



<p class="is-style-iw-2em">神经元接收到来自其他神经元传递过来的输入信号，这些输入信号通过带权重的连接(connection) 进行传递 ，神经 接收到的总输入值将与神经元的阀值进行比较，，然后通过&#8221;激活函数&#8221; (activation function 处理以产生神经元的输出。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220209141857496.png" alt="image-20220209141857496"/></figure>



<h2 class="wp-block-heading">激活函数</h2>



<p class="is-style-iw-2em">激活函数的作用是可以引入非线性因素，解决线性模型所不能解决的问题。</p>



<p class="is-style-iw-2em">首先我要将下面的三角形和圆形点进行正确的分类</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-53bdcef3616b928b33b24418483c3d61_720w.jpg" alt="img"/></figure>



<p class="is-style-iw-2em">比如一个感知机可以画出一条线</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/2e83b4403f21654cd9147f13ecfaf799_720w.jpg" alt="img"/></figure>



<p class="is-style-iw-2em">多个感知机组合还是线性化分</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/ef7eb0f56730058e1100dd6605eb2a25_720w.jpg" alt="img"/></figure>



<p class="is-style-iw-2em">但是无论怎么划分，还是无法用线性组合把这个问题解决。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-8026ccec915a9e8bbf19a4f4dfd913a0_720w.jpg" alt="img"/></figure>



<p class="is-style-iw-2em">加上一个激活函数后，就可以做出非线性的划分了</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/32cbeac5eaea9d655b9a50e4d8d0a687_720w.jpg" alt="img"/></figure>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/fab8a7ae1cb63992f70e160d7f03c067_720w.jpg" alt="img"/></figure>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/v2-9e3af4e88d56b4e034bd07688dd50bbe_720w.jpg" alt="img"/></figure>



<p class="is-style-iw-2em">所以<strong>加入激活函数是用来加入非线性因素的，解决线性模型所不能解决的问题</strong></p>



<p class="is-style-iw-2em">Sigmoid 函数把可能在较大 范围内变化的输入值挤压到 (0 1) 输出值范围内</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220208204523029.png" alt="image-20220208204523029"/></figure>



<h2 class="wp-block-heading">感知机和多层网络</h2>



<p class="is-style-iw-2em">上面已经提到过感知机了，感知机由两层神经网络组成，输入层接收外界输入信号后传递给输出层，其中输出层也叫&#8221;阈值逻辑单元&#8221; 。输入层只进行输入，不进行函数处理。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220209143619371.png" alt="感知机"/></figure>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220209143847864.png" alt="image-20220209143847864"/></figure>



<p class="is-style-iw-2em">感知机不同输入的权重通过上面的式子进行调整，学习率通常设置成一个小正数，比如0.1。</p>



<h2 class="wp-block-heading">多层前馈神经网络</h2>



<p class="is-style-iw-2em">每层神经元与下层完全互联，不存在跨层连接。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220209144221985.png" alt="image-20220209144221985"/></figure>



<h2 class="wp-block-heading">反向传播算法</h2>



<h3 class="wp-block-heading">BP算法</h3>



<p class="is-style-iw-2em"><strong>误差逆传播算法</strong>(error BackPropagation，BP算法）：BP 算法基于梯度下降策略， 以目标的负梯度方向对参数进进行调整。由于负梯度方向是函数值下降最快的方向，因此梯度下降法就是沿着负梯度方向搜索最优解。用梯度下降计算每一层最小的loss，然后根据求解出的参数调整前一层的权值得到最小的的损失。这个过程需要重复直到损失不再减少。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220209150541238.png" alt="image-20220209150541238"/></figure>



<p class="is-style-iw-2em">在100轮的时候已经划分出来全部的样本</p>



<h3 class="wp-block-heading">累积 BP 算法</h3>



<p class="is-style-iw-2em">累积 BP 算法直接针对累积误差最小化，它在读取整个训练集 一遍后才对参数进行更新， 其参数更新的频率低得多。但在很多任务中，累积误差下降到一定程度之后进 一步下降会非常缓慢，这时标准 BP 往往会更快获得较好的解，尤其是在训练 非常大时更明显。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>[Hornik et al., 1989] 证明，只需二个包含足够多神经元的隐层，多层前馈网络就能以任意精度逼近任意复杂度的连续函数.</p></blockquote>



<p class="is-style-iw-2em">这个就类似于用无限多的直线就可以趋近任何曲线。</p>



<h3 class="wp-block-heading">BP算法的过拟合问题</h3>



<h4 class="wp-block-heading">早停</h4>



<p class="is-style-iw-2em">将数据分成训练集和验证集，训练集用来计算梯度、更新连接权和阔值，验证集用来估计误差，若训练集误差降低验证集误差升高则停止训练。</p>



<h4 class="wp-block-heading">正则化</h4>



<p class="is-style-iw-2em">在误差目标函数中增加一个用于描述网络复杂度的部分，训练过程将 会偏好比较小的连接权和阈值，使网络输出更加 &#8220;光滑&#8221;从而对过拟合有所缓解。</p>



<h2 class="wp-block-heading">寻找最小误差</h2>



<p class="is-style-iw-2em">如何确定计算出的梯度极小值就是最优解呢？下面三个方法：</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220209152053197.png" alt="image-20220209152053197"/></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2022/4147/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4147</post-id>	</item>
		<item>
		<title>347. 前 K 个高频元素</title>
		<link>https://www.cztcode.com/2022/4142/</link>
					<comments>https://www.cztcode.com/2022/4142/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Thu, 03 Feb 2022 14:45:32 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=4142</guid>

					<description><![CDATA[这个题用到了小顶堆，之前用的比较少所以记录一下。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em"><a href="https://leetcode-cn.com/problems/top-k-frequent-elements/" target="_blank" rel="noopener">347. 前 K 个高频元素</a></p>



<p class="is-style-iw-2em">这个题用到了小顶堆，之前用的比较少所以记录一下。</p>



<h1 class="wp-block-heading">priority_queue 优先队列</h1>



<p class="is-style-iw-2em">C++实现堆有现成的容器适配器：priority_queue。</p>



<p class="is-style-iw-2em">优先队列具有队列的所有特性，包括基本操作，只是在这基础上添加了内部的一个排序，它本质是一个堆实现的。</p>



<p class="is-style-default">定义：priority_queue&lt;Type, Container, Functional><br>Type 就是数据类型，Container 就是容器类型（Container必须是用数组实现的容器，比如vector,deque等等，但不能用 list。STL里面默认用的是vector），Functional 就是比较的方式，当需要用自定义的数据类型时才需要传入这三个参数，使用基本数据类型时，只需要传入数据类型，默认是大顶堆。</p>



<pre class="wp-block-code"><code>//升序队列
priority_queue &lt;int,vector&lt;int>,greater&lt;int> > q;
//降序队列
priority_queue &lt;int,vector&lt;int>,less&lt;int> >q;

//greater和less是std实现的两个仿函数（就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator()，这个类就有了类似函数的行为，就是一个仿函数类了）
</code></pre>



<p class="is-style-iw-2em">写一个自定义operator构造小顶堆的例子，注意小顶堆是用greater：</p>



<pre class="wp-block-code"><code>priority_queue&lt;int, vector&lt;int>, greater&lt;int> > q;
for( int i= 0; i&lt; 10; ++i ) q.push(10-i);
while( !q.empty() ){
    cout &lt;&lt; q.top() &lt;&lt; endl;
    q.pop();
}
输出如下：
1
2
3
4
5
6
7
8
9
10</code></pre>



<p class="is-style-iw-2em">自定义一个pair类型的小顶堆：</p>



<pre class="wp-block-code"><code>#include&lt;iostream>
#include &lt;queue>

using namespace std;
class mycomparison {
public:
    bool operator() (const pair&lt;int,int>&amp; lhs,const pair&lt;int,int>&amp; rhs){
        return lhs.second>rhs.second;
    }
};

int main(){
    priority_queue&lt;pair&lt;int,int>,vector&lt;pair&lt;int,int>>,mycomparison> que;
    que.push({1,2});
    que.push({1,7});
    que.push({2,4});
    while (!que.empty()){
        cout&lt;&lt;que.top().second&lt;&lt; endl;
        que.pop();
    }
}

输出：
2
4
7

</code></pre>



<h1 class="wp-block-heading">题解</h1>



<p class="is-style-iw-2em">这个题首先用hash表记录每个数字出现的频率，然后维护一个大小为k的小顶堆（因为最后要留下最大的k个元素，小顶堆每次把小的元素弹出，留下的就是最大的k个了），最后把小顶堆的元素倒序输出即可。</p>



<pre class="wp-block-code"><code>class Solution {
public:
    class mycomparison {
        public:
        bool operator() (const pair&lt;int,int>&amp; lhs,const pair&lt;int,int>&amp; rhs){
            return lhs.second>rhs.second;
        }
    };
    vector&lt;int> topKFrequent(vector&lt;int>&amp; nums, int k) {
        unordered_map&lt;int, int> map;
        for(int i=0;i&lt;nums.size();i++){
            map&#91;nums&#91;i]]++;
        }
        priority_queue&lt;pair&lt;int,int>, vector&lt;pair&lt;int,int>>,mycomparison>priority_que;

        for(unordered_map&lt;int, int>::iterator it=map.begin();it!=map.end();it++){
            priority_que.push(*it);
            if(priority_que.size()>k){
                priority_que.pop();
            }
        }

        vector&lt;int> result(k);
        for(int i=k-1;i>=0;i--){
            result&#91;i]=priority_que.top().first;
            priority_que.pop();
        }
        return result;
    }
};</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2022/4142/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4142</post-id>	</item>
		<item>
		<title>机器学习第二章</title>
		<link>https://www.cztcode.com/2022/4140/</link>
					<comments>https://www.cztcode.com/2022/4140/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Thu, 03 Feb 2022 11:19:36 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=4140</guid>

					<description><![CDATA[第二章讲解的是机器学习模型评价的标准]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em"><strong>错误率：</strong> 把分类错误的样本数占样本总数的比例称为错误率</p>



<p class="is-style-iw-2em"><strong>精确率：</strong>1-错误率</p>



<p class="is-style-iw-2em"><strong>过拟合</strong>：把样本自身的一些特点当成所有样品都有的一般性质</p>



<p class="is-style-iw-2em"><strong>欠拟合：</strong>样本的一些性质尚未学好</p>



<p class="is-style-iw-2em">训练集与测试集划分：</p>



<p class="is-style-iw-2em"><strong>留出法：</strong>随机样本，取其中的2/3~4/5为训练集。<strong>分层采样：</strong> 则保留类别比例的采样方式。 如通过对 进行分层采样而获得含 70% 样本的训练集 和含 30% 样本的测试集 包含 500 个正例、 500 个反例，则分层采样得到的 应包含 350 个正例、 350 个反例。</p>



<p class="is-style-iw-2em"><strong>交叉验证法：</strong>将样本分为k个子集，其中k-1用于训练集，共进行k次训练。</p>



<p class="is-style-iw-2em"><strong>留一法：</strong>k个样本分为k个子集，每次只用一个验证，是交叉验证法的特例，留一法结果往往认为比较准确，受到样本规模影响产生误差比较小。</p>



<p class="is-style-iw-2em"><strong>自助法：</strong>自助法为了解决样本规模不一致造成的误差，每次从样本集D中取出一个样本放入D`作为样本，再将该样本放回D，这样m次采样中仍有近1/3的数据作为测试集。自助法在样本集较小时使用， 自助法产生的数据集改变了初始数据集的分布，这会引入估计偏差。</p>



<p class="is-style-iw-2em"><strong>最终模型：</strong> 模型选择完成后，学习算法和参数配置己选定，此时应该用数据集重新训练模型.这个模型在训练过程中使用了所有样本，这才是我们最终提交给用 户的模型 。</p>



<p class="is-style-iw-2em"><strong>分类混淆矩阵：</strong></p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/ffbe1563-4943-4702-976f-4f10e9076e2a.png" alt="ffbe1563-4943-4702-976f-4f10e9076e2a"/></figure>



<p class="is-style-iw-2em">查准率和查全率是一对矛盾的度量。一般来说，查准率高时，查全率往往偏低。而查全率高时，查准率往往偏低。通常只有在一些简单任务中 才可能使查全率和查准率都很高.</p>



<h2 class="wp-block-heading">比较两个模型的性能</h2>



<p class="is-style-iw-2em">P-R曲线</p>



<p class="is-style-iw-2em">P：Precision 查准率</p>



<p class="is-style-iw-2em">R: Recall 查全率（也叫召回率）</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/c3d25e48-1af9-4002-bcc2-c550441edaa6.png" alt="c3d25e48-1af9-4002-bcc2-c550441edaa6"/></figure>



<p class="is-style-iw-2em">若一个学习器的 P-R 曲线被另一个学习器的曲线完全&#8221;包住 则可断言后者的性能优于前者。</p>



<p class="is-style-iw-2em"><strong>平衡点（BEP）：</strong>查准率=查全率时的取值。</p>



<p class="is-style-iw-2em"><strong>F1度量：</strong></p>



<p class="is-style-iw-2em">F1是PR的调和平均数，整理后得到下面这个式子</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/523d1bd2-a3e0-4bfc-b394-6849b7520443.png" alt="523d1bd2-a3e0-4bfc-b394-6849b7520443"/></figure>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/46ceac6a-07d7-4d1b-ac52-2c7b06bcaef4.png" alt="46ceac6a-07d7-4d1b-ac52-2c7b06bcaef4"/></figure>



<p class="is-style-iw-2em">在n个样本上获得多个二分类混淆矩阵，最后计算的时候取平均，取平均的时候可以直接F1取平均值，也可以计算一个平均的二分类混淆矩阵再取平均，得到微F1。</p>



<p class="is-style-iw-2em"><strong>ROC受试者工作特征：</strong>很多机器学习产生的是一个值，这个值与分类阈值比较，大于归为正类，反之归为反类。若重视查准率，则阈值比较高。若重视查全率，则阈值比较低。ROC以真正率TPR为纵坐标，假正例率FPR为横坐标作图。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/ac70b179-cc6a-4797-b10a-1f4e59f3013f.png" alt="ac70b179-cc6a-4797-b10a-1f4e59f3013f"/></figure>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/43e8e6f8-d093-4507-9063-d8e70f45f4c3.png" alt="43e8e6f8-d093-4507-9063-d8e70f45f4c3"/></figure>



<p class="is-style-iw-2em">机器学习器的比较时， P-R 图相似， 一个学习器的 ROC 曲线被另 习器的曲线完全&#8221;包住&#8221;， 则可断言后者的性能优于前者;若两个学习 ROC 曲线发生交叉，则难以-般性地断言两者孰优孰 此时如果一定要进 行比较 则较为合理的判据是 比较 ROC 线下 的面积。</p>



<p class="is-style-iw-2em"><strong>PR和ROC曲线应用范围：</strong></p>



<p class="is-style-iw-2em">1.当正负样本比例差不多的时候，两者区别不大。</p>



<p class="is-style-iw-2em">2.PR曲线比ROC曲线更加关注正样本，而ROC则兼顾了两者。</p>



<p class="is-style-iw-2em">3.AUC越大，反映出正样本的预测结果更加靠前。（推荐的样本更能符合用户的喜好）</p>



<p class="is-style-iw-2em">4.当正负样本比例失调时，比如正样本1个，负样本100个，则ROC曲线变化不大，此时用PR曲线更加能反映出分类器性能的好坏。</p>



<p class="is-style-iw-2em">5.PR曲线和ROC绘制的方法不一样。</p>



<p class="is-style-iw-2em"><strong>代价曲线 </strong>：FP和FN的错误代价是不同的，代价曲线估计不同算法的代价。</p>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220205193017862.png" alt="image-20220205193017862"/></figure>



<figure class="wp-block-image"><img decoding="async" src="https://markdown.cztcode.com/image-20220205183609488.png" alt="image-20220205183609488"/></figure>



<p class="is-style-iw-2em">代价曲线的变量是Pcost（归一化的p，代表阈值），我们想知道取不同阈值对于两类错误的代价是多少，所以每取一个p，对应纵轴一个代价，围成的面积构成代价的期望。</p>



<h2 class="wp-block-heading">习题</h2>



<p class="is-style-iw-2em"><a href="https://zhuanlan.zhihu.com/p/35016406" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/35016406</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2022/4140/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4140</post-id>	</item>
		<item>
		<title>机器学习</title>
		<link>https://www.cztcode.com/2022/4137/</link>
					<comments>https://www.cztcode.com/2022/4137/#comments</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Wed, 02 Feb 2022 02:00:49 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=4137</guid>

					<description><![CDATA[最近写毕业设计，开始看周志华的西瓜书。写一写笔记把新名词记录下，方便日后复习。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">最近写毕业设计，开始看周志华的西瓜书。写一写笔记把新名词记录下，方便日后复习。</p>



<h1 class="wp-block-heading">第一章</h1>



<p class="is-style-iw-2em">预测的是离散值成为<strong>分类</strong>，预测的是连续值成为<strong>回归</strong>， 对只涉及两个类别的&#8221;二分 类&#8221; (binary cl sification) 任务，通常称其中一个类为 &#8220;正类&#8221; 。另一个类为&#8221;反类&#8221; (negative class); 涉及多个类别时，则称为&#8221;多分 类&#8221; (multi-class classification）叫任务 。 </p>



<p class="is-style-iw-2em"><strong>聚类</strong>：将训练集中的数据分成若干 组，每组称为 个&#8221;簇&#8221; (cluster); 根据训练数据是否有标记信息，分为监督学习和无监督学习，分类和回归是前者的代表，聚类是后者的代表。 泛化能力：学得的模型适用于新样本。 </p>



<p class="is-style-iw-2em"><strong>版本空间</strong>：与训练值一致的假设集合。 </p>



<p class="is-style-iw-2em"><strong>偏好/归纳偏好</strong>： 算法在学习过程中对某种类型假设的偏好。 任何一个有效的机器学习算法必有其归纳偏好，否则它将被假设空间中看 似在训练集上&#8221;等效&#8221;的假设所迷惑，而无法产生确定的学习结果.。</p>



<p class="is-style-iw-2em"> <strong>奥卡姆剃刀： </strong>若有多个假设与观察一致，则选最简单的那个。 对于一个算法，在某些问题上算法A比算法B好，则必然在另一些问题上，算法B比算法A好。 </p>



<p class="is-style-iw-2em"><strong>没有免费的午餐（NFL） ：</strong> 无论学习算法A多聪明、学习算法B多笨拙，它们的期望性相同。 NFL 走理最重要的寓意是让我们清楚地认识到，脱离具体问题，空泛地谈论&#8221;什么学习算法更好&#8221;毫无意义，因为若考虑所有潜在的问题。所有学习算法都一样好。要谈论算法的相对优劣，必须要针对具体的学习问题；在某些问题上表现好的学习算法，在另一些问题上却可能不尽如人意，学习算法自身的归纳偏好与问题是否相配，往往会起到决定性的作用。</p>



<p class="is-style-iw-2em"> <a rel="noreferrer noopener" href="https://zhuanlan.zhihu.com/p/44279394" target="_blank">机器学习 | 机器学习能在互联网搜索的哪些环节起什么作用 &#8211; 知乎 (zhihu.com)</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2022/4137/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4137</post-id>	</item>
		<item>
		<title>搜索入门</title>
		<link>https://www.cztcode.com/2021/4122/</link>
					<comments>https://www.cztcode.com/2021/4122/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sun, 31 Oct 2021 02:02:52 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=4122</guid>

					<description><![CDATA[搜索 1.定义 搜索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况，从而求出问题的解的一种方法。现阶段一般有枚举算法、深度优先搜索、广度优先搜索、A* 算法、回溯算法、蒙特卡洛树搜索、散列函数等算法。在大规模实验环境中，通常通过在搜索前，根据条件降低搜索规模；根据问题的约束条件进行剪枝；利用搜索过程中的中间解，避免重复计算这几种方法进行优化。（来自百度百科） 2.运行原 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<h1 class="wp-block-heading">搜索</h1>



<h2 class="wp-block-heading">1.定义</h2>



<p class="is-style-iw-2em">搜索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况，从而求出问题的解的一种方法。现阶段一般有枚举算法、深度优先搜索、广度优先搜索、A* 算法、回溯算法、蒙特卡洛树搜索、散列函数等算法。在大规模实验环境中，通常通过在搜索前，根据条件降低搜索规模；根据问题的约束条件进行剪枝；利用搜索过程中的中间解，避免重复计算这几种方法进行优化。（来自百度百科）</p>



<h2 class="wp-block-heading">2.运行原理</h2>



<p class="is-style-iw-2em">搜索算法实际上是根据初始条件和扩展规则构造一棵“解答树”并寻找符合目标状态的节点的过程。不同的搜索算法不同的只是拓展节点的方式和拓展的节点，而所有的算法优化和改进主要都是通过修改其控制结构来完成的。其实，在这样的思考过程中，我们已经不知不觉地将一个具体的问题抽象成了一个图论的模型——树，即搜索算法的使用第一步在于搜索树的建立</p>



<p class="is-style-iw-2em">由图可以知道，这样形成的一棵树叫搜索树。初始状态对应着根节点，目标状态对应着目标结点。排在前的结点叫父结点，其后的结点叫子结点，同一层中的结点是兄弟结点，由父结点产生子结点叫扩展。完成搜索的过程就是找到一条从根结点到目标结点的路径，找出一个最优的解。这种搜索算法的实现类似于图或树的遍历，通常可以有两种不同的实现方法，即深度优先搜索（DFS——Depth First search）和广度优先搜索（BFS——Breadth First Search）。</p>



<figure class="wp-block-image"><img decoding="async" src="https://enddonkey.oss-cn-beijing.aliyuncs.com/博客/20211031012822.webp" alt="" /></figure>



<h2 class="wp-block-heading">3.主要类别</h2>



<h3 class="wp-block-heading">深度优先搜索（dfs）</h3>



<p class="is-style-iw-2em">深度优先搜索，顾名思义，即尽可能向深处进行搜索，即选定一条确定的路径，不断地向子节点进行搜索，直到到达末节点或者到达答案，并且在探索过程中，一旦发现原来的选择不符合要求，就回溯至父亲结点重新选择另一结点，继续向前探索，如此反复进行，直至求得最优解。深度优先搜索的实现方式可以采用递归或者栈来实现。</p>



<h4 class="wp-block-heading">相关剪枝思想</h4>



<p class="is-style-iw-2em">为了缩短搜索时间我们可以想到一些方法去减搜索的纸条，从而减少节点的搜索，主要可以从以下几点出发</p>



<p class="is-style-iw-2em">(1)减少节点数，思想：尽可能减少生成的节点数</p>



<p class="is-style-iw-2em">(2)定制回溯边界，思想：定制回溯边界条件，剪掉不可能得到最优解的子树</p>



<p class="is-style-iw-2em">在我们平时看来显而易见不可能出现最优解的情况对于计算机而言，仍然会去搜索，判断，因此我们需要合理使用剪枝条，排除掉一些不可能出现最优解的情况，这也同样是后期学习搜索需要考虑的方法</p>



<h3 class="wp-block-heading">广度优先搜索（bfs）</h3>



<p class="is-style-iw-2em">广度优先搜索，主要的点在于广字上，它与深度优先搜索的不同在于，深度优先搜索是一搜到底的搜索，而广度优先搜索在于广泛，其过程为：首先访问初始点Vi，并将其标记为已访问过，接着访问Vi的所有未被访问过可到达的邻接点Vi1、Vi2……Vit，并均标记为已访问过，然后再按照Vi1、Vi2……Vit的次序，访问每一个顶点的所有未被访问过的邻接点，并均标记为已访问过，依此类推，直到图中所有和初始点Vi有路径相通的顶点都被访问过为止。</p>



<p class="is-style-iw-2em">换而言之，广度优先搜索是一层一层的搜索，而深度优先搜索是一条线直接到底，相比而言，在部分情况下广度优先搜索有着更好的性能，部分情况下则是深度优先搜索，下面我们将讲解何时应该使用广度优先搜索，而何时应该使用深度优先搜索。</p>



<p class="is-style-iw-2em">下面则是一些优化（在本文中不具体讲述各种搜索优化，只作为入门引入）</p>



<figure class="wp-block-image"><img decoding="async" src="https://enddonkey.oss-cn-beijing.aliyuncs.com/博客/20211031015759.png" alt="" /></figure>



<h2 class="wp-block-heading">3.例题</h2>



<h3 class="wp-block-heading">P1149 [NOIP2008 提高组] 火柴棒等式</h3>



<figure class="wp-block-image"><img decoding="async" src="https://enddonkey.oss-cn-beijing.aliyuncs.com/博客/20211031020117.png" alt="" /></figure>



<figure class="wp-block-image"><img decoding="async" src="https://enddonkey.oss-cn-beijing.aliyuncs.com/博客/20211031020219.png" alt="" /></figure>



<p class="is-style-iw-2em">对于这道题目，我们可以发现，它给出所拥有的火柴棒个数，要求得出可能方案，也就是说无论使用广搜还是深搜都是需要搜遍整棵搜索树的，然而如果使用bfs，我们不方便判断每一层划分的参数，同样我们不能立即减去不可能出现的情况，因此我们这题使用的是深搜。</p>



<pre class="wp-block-code"><code>#include&lt;bits/stdc++.h&gt;
using namespace std;
int n,ans,a&#091;15000]={6,2,5,5,4,5,6,3,7,6,8},book&#091;10];
void dfs(int num,int data){
    if(num&gt;3){
        if(book&#091;1]+book&#091;2]==book&#091;3]&amp;&amp;!data){
            ans++;
            //printf("%d+%d=%d\n",book&#091;1],book&#091;2],book&#091;3]);
        } 
        return;
    }
    for(int i=0;i&lt;=1111;++i){
        if(a&#091;i]&lt;=data){
            book&#091;num]=i;
            dfs(num+1,data-a&#091;i]);
            book&#091;num]=-1; 
        }
    }
}
int main(){
    scanf("%d",&amp;n);
    if(n&lt;=10) printf("0");
    else{
        //memset(book,-1,sizeof(book));
        for(int i=11;i&lt;=1111;++i){
            a&#091;i]=a&#091;i/10]+a&#091;i%10];
        }
        dfs(1,n-4);
        printf("%d",ans);

    }   
    return 0;
}</code></pre>



<p class="is-style-iw-2em">解决搜索题目大致思路</p>



<p class="is-style-iw-2em">1.确定使用哪种搜索方法</p>



<p class="is-style-iw-2em">2.确定函数所带变量</p>



<p class="is-style-iw-2em">3.确认临界条件</p>



<p class="is-style-iw-2em">4.下一步搜索与回溯</p>



<p class="is-style-iw-2em">在搜索前，我们需要做一系列的预处理，即得出每一个数字所出现所需要的火柴数，我们又知道只要我们知道了前10个数字，那么我们变掌握的所有的数字所需要的火柴棒。</p>



<p class="is-style-iw-2em">并且我们可以先思考到一个数最大可以有多大，首先减去符号位 4 便是 20 其次，一个数需要进行加法，而最少的火柴棒便是1 即 1111+1111=2222</p>



<p class="is-style-iw-2em">其次我们开始设定搜索所需要的参数，第一个是目前在寻找第几个数，而第二个意味着还有多少根火柴棒。接着，我们需要确定临界条件，对于 <em>num</em> 如果我们当前寻找的数大于3，那么说明我们已经寻找完毕，同时我们需要判断此时剩下的火柴棒是否为0即可。紧接着我们进行下一步搜索与回溯，即如何找到下一个数字，我们可以进行一次遍历，将所有符合当前火柴棒的数字全部都进行一次搜索即可。同时进行记录与回溯。</p>



<h3 class="wp-block-heading">P1219 [USACO1.5]八皇后 Checker Challenge</h3>



<figure class="wp-block-image"><img decoding="async" src="https://enddonkey.oss-cn-beijing.aliyuncs.com/博客/20211031091227.png" alt="" /></figure>



<figure class="wp-block-image"><img decoding="async" src="https://enddonkey.oss-cn-beijing.aliyuncs.com/博客/20211031091324.png" alt="" /></figure>



<p class="is-style-iw-2em">对于这道题目，我们同样使用的是深搜，即不断尝试每一个棋子的位置，并对其进行回溯。</p>



<pre class="wp-block-code"><code>#include&lt;bits/stdc++.h&gt;
using namespace std;
int n,ans,a&#091;100],b&#091;100],c&#091;100],d&#091;100];
void out(){
    if(ans&lt;=2){
        for(int i=1;i&lt;=n;++i){
            printf("%d ",a&#091;i]);
        }
        printf("\n");
    }
    ans++;
}
void dfs(int x){
    if(x&gt;n){
        out();
        return;
    }
    for(int j=1;j&lt;=n;++j){
        if((!b&#091;j])&amp;&amp;(!c&#091;x+j])&amp;&amp;(!d&#091;x-j+n])){
            a&#091;x]=j;
            b&#091;j]=1;
            c&#091;x+j]=1;
            d&#091;x-j+n]=1;
            dfs(x+1);
            a&#091;x]=0;
            b&#091;j]=0;
            c&#091;x+j]=0;
            d&#091;x-j+n]=0;
        }
    }


}
int main(){
    scanf("%d",&amp;n);
    dfs(1);
    printf("%d",ans);
    return 0;
} </code></pre>



<p class="is-style-iw-2em">我们先确定变量，对于每一行我们都只能放一个棋子，那么我们可以将行数作为变量，而当行数大于当前所给出的n，那么说明已经搜索完毕，对于此题，在行数确定的情况下，我们只需要不断尝试每一列可以放在哪里即可，此时需要进行一次判断，判断这一列以及其所在对角线是否可行，我们通过观察可以发现 左对角线的编号可以写作（x+j），而右对角线可以写作 （x-j+n），则我们找到一个合理存在的点，并对其进行放置，并且标记它所在的列，对角线后进行下一次搜索以及回溯。</p>



<p class="is-style-iw-2em">同样对于这题，我们可以用二进制数来表示状态，而不是用数组，原理是二进制数本身可以看作一个bool 类型的数组，有0/1 两种形式，可以用来表示状态。</p>



<pre class="wp-block-code"><code>#include&lt;bits/stdc++.h&gt;
using namespace std;
int n,add,book,line,data,ans&#091;10];
bool getbit(int x,int i){
    return  !((x&gt;&gt;i)&amp;1);
}
void dfs(int m){
    if(m==n+1){
        add++;
        if(add&lt;=3){
            for(int i=1;i&lt;=n;i++){
                printf("%d",ans&#091;i]);
                if(i&lt;n) printf(" ");
            }printf("\n");
        } 
    } 
    else{
        for(int i=1;i&lt;=n;i++){
            if(getbit(line,i)&amp;&amp;getbit(data,i-m+n)&amp;&amp;getbit(book,i+m-1)){
                line|=(1&lt;&lt;i);
                book|=(1&lt;&lt;(i+m-1));
                data|=(1&lt;&lt;(i-m+n));
                ans&#091;m]=i;
                dfs(m+1);
                line&amp;=~(1&lt;&lt;i);
                book&amp;=~(1&lt;&lt;(i+m-1));
                data&amp;=~(1&lt;&lt;(i-m+n));
            }
        }
    }
    return;
}
int main(){
    scanf("%d",&amp;n);
    dfs(1);printf("%d",add);
    return 0;
} </code></pre>



<p class="is-style-iw-2em">此处涉及部分二进制运算，可以百度查看 <a href="https://baike.baidu.com/item/%E4%BA%8C%E8%BF%9B%E5%88%B6%E8%BF%90%E7%AE%97" target="_blank" rel="noopener">二进制</a></p>



<h3 class="wp-block-heading">P1451 求细胞数量</h3>



<figure class="wp-block-image"><img decoding="async" src="https://enddonkey.oss-cn-beijing.aliyuncs.com/博客/20211031092635.png" alt="" /></figure>



<p class="is-style-iw-2em">对于这题，我们需要从一个点向其它点进行扩散，判断是否为一个细胞，而深搜只能一搜到底。</p>



<pre class="wp-block-code"><code>#include&lt;bits/stdc++.h&gt;
using namespace std;
const int dx&#091;4]={0,1,0,-1};
const int dy&#091;4]={1,0,-1,0};
int m,n,ans;
int mp&#091;101]&#091;101];
int vis&#091;101]&#091;101];
struct node
{
    int x,y;
};
queue &lt;node&gt; q;
void bfs(int x,int y)
{
    node sta;
    sta.x=x;
    sta.y=y;
    q.push(sta);
    vis&#091;x]&#091;y]=1;
    while(!q.empty())
    {
        node fr=q.front();
        vis&#091;fr.x]&#091;fr.y]=1;
        q.pop();
        for(int i=0;i&lt;4;i++)
        {
            node son;
            son.x=fr.x+dx&#091;i];
            son.y=fr.y+dy&#091;i];
            if(mp&#091;son.x]&#091;son.y]==0||vis&#091;son.x]&#091;son.y])
            continue;
            else if(mp&#091;son.x]&#091;son.y]&amp;&amp;(!vis&#091;son.x]&#091;son.y]))
            {
                q.push(son);
            }

        }
    }
    return;
}

int main()
{
    cin&gt;&gt;n&gt;&gt;m;
    for(int i=1;i&lt;=n;i++)
    {
        for(int j=1;j&lt;m;j++)
        {
            scanf("%1d",&amp;mp&#091;i]&#091;j]);
            if(mp&#091;i]&#091;j]&gt;0)
            mp&#091;i]&#091;j]=1;
        }
    }

    for(int i=1;i&lt;=n;i++)
    {
        for(int j=1;j&lt;=m;j++)
        {
            if(mp&#091;i]&#091;j]&amp;&amp;(!vis&#091;i]&#091;j])){
                bfs(i,j);
                ans++;
            } 
        }
    }
    cout&lt;&lt;ans+1;
    return 0;
}</code></pre>



<p class="is-style-iw-2em">对于广搜，我们需要使用一个队列，来进行储存进行搜索的顺序，即当一个点搜索完毕的时候，将与它相邻的其它点加入队列进行下一次搜索并且重复上述过程。</p>



<p class="is-style-iw-2em">首先是预处理，将细胞数字全部标记为 1，紧接着对于每一个为1的点进行搜索，判断周围的点是否为1，并且加入队列，不断进行扩散。参数为点的横纵坐标即可。此处注意广搜并不需要回溯这步操作，而只需要将点加入队列即可。</p>



<h3 class="wp-block-heading">P1256 显示图像</h3>



<figure class="wp-block-image"><img decoding="async" src="https://enddonkey.oss-cn-beijing.aliyuncs.com/博客/20211031095001.png" alt="" /></figure>



<figure class="wp-block-image"><img decoding="async" src="https://enddonkey.oss-cn-beijing.aliyuncs.com/博客/20211031095102.png" alt="" /></figure>



<p class="is-style-iw-2em">此处的思路与上述思路相近，不做过多阐述，直接放代码</p>



<pre class="wp-block-code"><code>#include&lt;bits/stdc++.h&gt;
using namespace std;
struct node{
    int x;
    int y;
};
node queu&#091;40000];
const int dx&#091;4]={-1,0,1,0};
const int dy&#091;4]={0,1,0,-1};
int a&#091;200]&#091;200],n,m,ans&#091;200]&#091;200],l=1,r=1;
void bfs(){
    if(l&gt;r) return;
    int nowx=queu&#091;l].x;
    int nowy=queu&#091;l].y;
    a&#091;nowx]&#091;nowy]=1;
    for(int i=0;i&lt;4;++i){
        int wx=nowx+dx&#091;i];
        int wy=nowy+dy&#091;i];
        if(!a&#091;wx]&#091;wy]&amp;&amp;wx&gt;=1&amp;&amp;wy&gt;=1&amp;&amp;wx&lt;=n&amp;&amp;wy&lt;=m){
            r++;
            queu&#091;r].x=wx;
            queu&#091;r].y=wy;
            ans&#091;wx]&#091;wy]=ans&#091;nowx]&#091;nowy]+1;
            a&#091;wx]&#091;wy]=1;
        }
    }
    l++;
    bfs();
}
int main(){
    scanf("%d %d",&amp;n,&amp;m);
    for(int i=1;i&lt;=n;++i){
        for(int j=1;j&lt;=m;++j){
            scanf("%1d",&amp;a&#091;i]&#091;j]);
            if(a&#091;i]&#091;j]){
                queu&#091;r].x=i;
                queu&#091;r].y=j;
                ans&#091;i]&#091;j]=0;
                r++;
            }
        }
    }
    bfs();
    for(int i=1;i&lt;=n;++i){
        for(int j=1;j&lt;=m;++j){
            printf("%d ",ans&#091;i]&#091;j]);
        }
        printf("\n");
    }
    return 0;
} </code></pre>



<p class="is-style-iw-2em">总结一下，对于搜索题目大致分为下面四步</p>



<p class="is-style-iw-2em">1.确定使用哪种搜索方法</p>



<p class="is-style-iw-2em">2.确定函数所带变量</p>



<p class="is-style-iw-2em">3.确认临界条件</p>



<p class="is-style-iw-2em">4.下一步搜索与回溯（对于广搜则为加入队列）</p>



<h2 class="wp-block-heading">其他例题</h2>



<p class="is-style-iw-2em"><a href="https://www.cnblogs.com/donkey2603089141/p/11416524.html" target="_blank" rel="noopener">[USACO07OCT]障碍路线 &amp; yzoj P1130 拐弯</a></p>



<p class="is-style-iw-2em"><a href="https://www.cnblogs.com/donkey2603089141/p/11416743.html" target="_blank" rel="noopener">yzoj P1948 取数字问题</a></p>



<p class="is-style-iw-2em"><a href="https://www.luogu.com.cn/problem/P3958" target="_blank" rel="noopener">P3958 [NOIP2017 提高组] 奶酪</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2021/4122/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">4122</post-id>	</item>
		<item>
		<title>POJ2353总结</title>
		<link>https://www.cztcode.com/2020/3593/</link>
					<comments>https://www.cztcode.com/2020/3593/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Fri, 09 Oct 2020 15:14:32 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3593</guid>

					<description><![CDATA[Ministry 两次dp]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading"><strong>Ministry</strong></h2>



<p><a class="rank-math-link" href="http://poj.org/problem?id=2353" target="_blank" rel="noopener">题目来源</a></p>



<h3 class="wp-block-heading">Description</h3>



<p>Mr. F. wants to get a document be signed by a minister. A minister signs a document only if it is approved by his ministry. The ministry is an M-floor building with floors numbered from 1 to M, 1&lt;=M&lt;=100. Each floor has N rooms (1&lt;=N&lt;=500) also numbered from 1 to N. In each room there is one (and only one) official.</p>



<p>A document is approved by the ministry only if it is signed by at least one official from the M-th floor. An official signs a document only if at least one of the following conditions is satisfied:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>a. the official works on the 1st floor;<br>b. the document is signed by the official working in the room with the same number but situated one floor below;<br>c. the document is signed by an official working in a neighbouring room (rooms are neighbouring if they are situated on the same floor and their numbers differ by one).</p></blockquote>



<p>Each official collects a fee for signing a document. The fee is a positive integer not exceeding 10^9.</p>



<p>You should find the cheapest way to approve the document.</p>



<h3 class="wp-block-heading">Input</h3>



<p>The first line of an input file contains two integers, separated by space. The first integer M represents the number of floors in the building, and the second integer N represents the number of rooms per floor. Each of the next M lines contains N integers separated with spaces that describe fees (the k-th integer at l-th line is the fee required by the official working in the k-th room at the l-th floor).</p>



<h3 class="wp-block-heading">Output</h3>



<p>You should print the numbers of rooms (one per line) in the order they should be visited to approve the document in the cheapest way. If there are more than one way leading to the cheapest cost you may print an any of them.</p>



<h3 class="wp-block-heading">Sample Input</h3>



<pre class="wp-block-preformatted">3 4
10 10 1 10
2 2 2 10
1 10 10 10
</pre>



<h3 class="wp-block-heading">Sample Output</h3>



<pre class="wp-block-preformatted">3
3
2
1
1
</pre>



<h3 class="wp-block-heading">Hint</h3>



<p>You can assume that for each official there always exists a way to get the approval of a document (from the 1st floor to this official inclusively) paying no more than 10^9.<br>This problem has huge input data,use scanf() instead of cin to read data to avoid time limit exceed.</p>



<h3 class="wp-block-heading">思路</h3>



<p class="is-style-iw-2em">由于可以选择的方向有向上,向右,向左三种,其中向左和向右是相反的方向,需要进行两次dp.只需要进行两次dp的缘由是每一趟dp,dp[i]的产生都是依据前一项dp[i-1]已经被更新过了:</p>



<pre class="wp-block-code"><code>for (int i=1;i&lt;m;i++){
                dp&#091;i]&#091;0]=dp&#091;i-1]&#091;0]+fee&#091;i]&#091;0];
		for (int j=1;j&lt;n;j++){//not 0..n-2
			dp&#091;i]&#091;j]=min(dp&#091;i-1]&#091;j],dp&#091;i]&#091;j-1])+fee&#091;i]&#091;j];
		}
		for (int j=n-2;j&gt;=0;j--){
			dp&#091;i]&#091;j]=min(dp&#091;i]&#091;j],dp&#091;i]&#091;j+1]+fee&#091;i]&#091;j]);
		}
	}</code></pre>



<p class="is-style-iw-2em">另外初始化时,每一层的第一个房间的初始化,并不是在循环开始之前就执行的,而是在循环进行中执行的,否则会错.</p>



<pre class="wp-block-code"><code>//	for (int i=1;i&lt;m;i++){
//		dp&#091;i]&#091;0]=dp&#091;i-1]&#091;0]+fee&#091;i]&#091;0];
//	}
	for (int i=1;i&lt;m;i++){
	dp&#091;i]&#091;0]=dp&#091;i-1]&#091;0]+fee&#091;i]&#091;0];
		for (int j=1;j&lt;n;j++){//not 0..n-2
			dp&#091;i]&#091;j]=min(dp&#091;i-1]&#091;j],dp&#091;i]&#091;j-1])+fee&#091;i]&#091;j];
		}
		for (int j=n-2;j&gt;=0;j--){
			dp&#091;i]&#091;j]=min(dp&#091;i]&#091;j],dp&#091;i]&#091;j+1]+fee&#091;i]&#091;j]);
		}
	}</code></pre>



<pre class="wp-block-code"><code>#include&lt;stdio.h&gt;
#include&lt;string.h&gt;
#include&lt;stack&gt;
#include&lt;algorithm&gt;
using namespace std;

int m,n;
int fee&#091;110]&#091;510];
int dp&#091;110]&#091;510];
stack&lt;int&gt; s;

int main(){
	int mini=0x3f3f3f3f;
	int pos=0;

	int level;
	memset(fee,0,sizeof(fee));
	memset(dp,0x3f3f3f3f,sizeof(dp));
	scanf("%d%d",&amp;m,&amp;n);
	for (int i=0;i&lt;m;i++){
		for (int j=0;j&lt;n;j++){
			scanf("%d",&amp;fee&#091;i]&#091;j]);
		}
	}
	for (int j=0;j&lt;n;j++){
		dp&#091;0]&#091;j]=fee&#091;0]&#091;j];
	}
//	for (int i=1;i&lt;m;i++){
//		dp&#091;i]&#091;0]=dp&#091;i-1]&#091;0]+fee&#091;i]&#091;0];
//	}
	for (int i=1;i&lt;m;i++){
	dp&#091;i]&#091;0]=dp&#091;i-1]&#091;0]+fee&#091;i]&#091;0];
		for (int j=1;j&lt;n;j++){//not 0..n-2
			dp&#091;i]&#091;j]=min(dp&#091;i-1]&#091;j],dp&#091;i]&#091;j-1])+fee&#091;i]&#091;j];
		}
		for (int j=n-2;j&gt;=0;j--){
			dp&#091;i]&#091;j]=min(dp&#091;i]&#091;j],dp&#091;i]&#091;j+1]+fee&#091;i]&#091;j]);
		}
	}
//	for (int i=0;i&lt;m;i++){
//		for (int j=0;j&lt;n;j++){
//			printf("%4.d",dp&#091;i]&#091;j]);
//		}
//		printf("\n");
//	}
	for (int j=0;j&lt;n;j++){
		if (dp&#091;m-1]&#091;j]&lt;mini){
			mini=dp&#091;m-1]&#091;j];
			pos=j;
		}
	}
	s.push(pos);
	level=m-1;
	int flag=-1;//-1:all,1:not left,2:not right
	while (level&gt;0){
		if (dp&#091;level]&#091;pos]-fee&#091;level]&#091;pos]==dp&#091;level-1]&#091;pos]){
			s.push(pos);
			level--;
			flag=-1;
		}else if (flag!=2&amp;&amp;pos&gt;0&amp;&amp;dp&#091;level]&#091;pos]-fee&#091;level]&#091;pos]==dp&#091;level]&#091;pos-1]){
			pos--;
			s.push(pos);
			flag=1;
		}else if (flag!=1&amp;&amp;pos&lt;n-1&amp;&amp;dp&#091;level]&#091;pos]-fee&#091;level]&#091;pos]==dp&#091;level]&#091;pos+1]){
			pos++;
			s.push(pos);
			flag=2;
		}else {
			printf("now:%d,left:%d,right:%d,down:%d,fee%d\n",dp&#091;level]&#091;pos],dp&#091;level]&#091;pos-1],dp&#091;level]&#091;pos+1],dp&#091;level-1]&#091;pos],fee&#091;level]&#091;pos]);
			printf("no res\n");
			break;
		}
	}
	while (!s.empty()){
		printf("%d\n",s.top()+1);
		s.pop();
	}
	return 0;
}</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3593/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3593</post-id>	</item>
		<item>
		<title>POJ2346总结</title>
		<link>https://www.cztcode.com/2020/3589/</link>
					<comments>https://www.cztcode.com/2020/3589/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Thu, 08 Oct 2020 14:37:24 +0000</pubDate>
				<category><![CDATA[算法]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3589</guid>

					<description><![CDATA[Lucky tickets 动态规划]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>


<h2 class="wp-block-heading"><strong>Lucky tickets</strong></h2>



<p><a class="rank-math-link" href="http://poj.org/problem?id=2346" target="_blank" rel="noopener">题目来源</a></p>



<h3 class="wp-block-heading">Description</h3>



<p>The public transport administration of Ekaterinburg is anxious about the fact that passengers don&#8217;t like to pay for passage doing their best to avoid the fee. All the measures that had been taken (hard currency premiums for all of the chiefs, increase in conductors&#8217; salaries, reduction of number of buses) were in vain. An advisor especially invited from the Ural State University says that personally he doesn&#8217;t buy tickets because he rarely comes across the lucky ones (a ticket is lucky if the sum of the first three digits in its number equals to the sum of the last three ones). So, the way out is found — of course, tickets must be numbered in sequence, but the number of digits on a ticket may be changed. Say, if there were only two digits, there would have been ten lucky tickets (with numbers 00, 11, &#8230;, 99). Maybe under the circumstances the ratio of the lucky tickets to the common ones is greater? And what if we take four digits? A huge work has brought the long-awaited result: in this case there will be 670 lucky tickets. But what to do if there are six or more digits?<br>So you are to save public transport of our city. Write a program that determines a number of lucky tickets for the given number of digits. By the way, there can&#8217;t be more than 10 digits on one ticket.</p>



<h3 class="wp-block-heading">Input</h3>



<p>Input contains a positive even integer N not greater than 10. It&#8217;s an amount of digits in a ticket number.</p>



<h3 class="wp-block-heading">Output</h3>



<p>Output should contain a number of tickets such that the sum of the first N/2 digits is equal to the sum of the second half of digits.</p>



<h3 class="wp-block-heading">Sample Input</h3>



<pre class="wp-block-preformatted">4</pre>



<h3 class="wp-block-heading">Sample Output</h3>



<pre class="wp-block-preformatted">670
</pre>



<pre class="wp-block-code"><code>#include&lt;stdio.h&gt;
#include&lt;string.h&gt;
int n,ans;
int num&#091;6]&#091;50];// num&#091;i]&#091;j]: the cases of use i dits to get summary j 
int main(){
	ans=0;
	memset(num,0,sizeof(num));
	scanf("%d",&amp;n);
	for (int i=0;i&lt;=9;i++){
		num&#091;1]&#091;i]=1;
	}
	for (int i=2;i&lt;=n/2;i++){
		for (int j=0;j&lt;=9*i;j++){// j is the summary this loop to form
			for (int k=0;k&lt;=9;k++){
				if (j-k&gt;=0){
					num&#091;i]&#091;j]+=num&#091;i-1]&#091;j-k];
				}
			}
		}
	}
	for (int i=0;i&lt;=9*n/2;i++){
		ans+=num&#091;n/2]&#091;i]*num&#091;n/2]&#091;i];
	}
	printf("%d\n",ans);
	return 0;
}</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3589/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3589</post-id>	</item>
	</channel>
</rss>
