python里的for循环[:-(topNfeat+1):-1]什么意思

相关内容推荐
about echojb.com爱脚本网(www.echojb.com)为你提供软件编程和硬件技术方面资料,信息,方法,是你完成项目及工作的好帮手。Groove Girl (Feat. Frequent)-Topnatc T.O.P.-单曲-酷我音乐-好音质用酷我
Groove Girl (Feat. Frequent)
该歌曲需使用客户端付费下载
Topnatc T.O.P.歌榜
该专辑其他歌曲
歌曲榜TOP10
举报电话:010- 举报邮箱:Python 与 机器学习Python 与 机器学习Python · 数学 · 机器学习关注专栏更多置顶文章最新文章{&debug&:false,&apiRoot&:&&,&paySDK&:&https:\u002F\u002Fpay.zhihu.com\u002Fapi\u002Fjs&,&wechatConfigAPI&:&\u002Fapi\u002Fwechat\u002Fjssdkconfig&,&name&:&production&,&instance&:&column&,&tokens&:{&X-XSRF-TOKEN&:null,&X-UDID&:null,&Authorization&:&oauth c3cef7c66aa9e6a1e3160e20&}}{&database&:{&Post&:{&&:{&title&:&Python 与 机器学习 · 简介&,&author&:&carefree0910&,&content&:&\u003Cp\u003E==========
更新 ========== \n\u003C\u002Fp\u003E\u003Cp\u003E最近写了个 Tensorflow 的 RNN 封装,应该可以拿它来讲讲 RNN 了\u003C\u002Fp\u003E\u003Cp\u003E主要是感觉现在网上的相关资源都太不友好了,特别是 Tensorflow 官网那个……所以就干脆自己弄了个(个人认为)比较友好的封装\n\u003C\u002Fp\u003E\u003Cp\u003E==========
更新 ==========\n\u003C\u002Fp\u003E\u003Cp\u003E今天搭建了一个\u003Ca href=\&https:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002Fwww.carefree0910.com\u002F\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E博客\u003C\u002Fa\u003E,那里讲得可能会更完整、更有条理一点\u003C\u002Fp\u003E\u003Cp\u003E==========
更新 ==========\u003C\u002Fp\u003E\u003Cp\u003E做了一个\u003Ca href=\&https:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fgithub.com\u002FcarefreeFImageRecognition\u002Ftree\u002Fmaster\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E图像分类小项目\u003C\u002Fa\u003E、具有一定的实用性,系列教程可以参见\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F?refer=carefree0910-pyml\& class=\&internal\&\u003E这里\u003C\u002Fa\u003E \u003C\u002Fp\u003E\u003Cp\u003E==========
更新 ==========\u003C\u002Fp\u003E\u003Cp\u003E写了一个比较朴素的 SVM,觉得应该能拿它来讲讲 SVM 了\u003C\u002Fp\u003E\u003Cp\u003E==========
更新 ==========\u003C\u002Fp\u003E\u003Cp\u003E写了一个比较朴素的随机森林,觉得应该能拿它来讲讲 RF 了\u003C\u002Fp\u003E\u003Cp\u003E========== 分割线的说 ==========\n\u003C\u002Fp\u003E\u003Cp\u003E(题图致敬 scikit-learn) \n\u003C\u002Fp\u003E\u003Cp\u003E感觉是个坑,但还是义无反顾地跳了进来(跳!\u003C\u002Fp\u003E\u003Cp\u003E机器学习在当今很火,可能有不少童鞋想要试一试这听上去很神奇的东西……写这个(可能其实不会有人看的)系列的目的,主要是想给不知如何下手的童鞋们介绍一种入门的姿势\u003C\u002Fp\u003E\u003Cp\u003E没错就是 Python,Python 是世界上最好的语言(逃\n关于 Python 的运用我也会写一个系列,目录在\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& class=\&internal\&\u003E这里\u003C\u002Fa\u003E \u003C\u002Fp\u003E\u003Cp\u003E先说一下最基本的内容吧(以后可能会更新……毕竟我本人也是在边学习边实现的)\u003C\u002Fp\u003E\u003Cp\u003E这里是 \u003Ca href=\&https:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Ftree\u002Fmaster\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EGitHub\u003C\u002Fa\u003E 地址,以下是(目前为止)我已经实现(并打算写专栏)的内容:\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E*\u003C\u002Fb\u003E \u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F?refer=carefree0910-pyml\& class=\&internal\&\u003ETensorflow 应用式入门教程\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E*\u003C\u002Fb\u003E \u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& class=\&internal\&\u003ENN\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E*\u003C\u002Fb\u003E \u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& class=\&internal\&\u003ECNN\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E*\u003C\u002Fb\u003E \u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& class=\&internal\&\u003ERNN\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E*\u003C\u002Fb\u003E \u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F?refer=carefree0910-pyml\& class=\&internal\&\u003E朴素贝叶斯\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E*\u003C\u002Fb\u003E \u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& class=\&internal\&\u003E决策树\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E*\u003C\u002Fb\u003E \u003Ca href=\&https:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Ftree\u002Fmaster\u002Fd_Ensemble\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E集成学习\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E*\u003C\u002Fb\u003E \u003Ca href=\&https:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Ftree\u002Fmaster\u002Fe_SVM\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003E支持向量机(SVM) \u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E目前差不多就这些,对于已经开始写的算法,\u003Cb\u003E上面相应的文字会变成相应的文章,否则会导向相应的 GitHub 地址\u003C\u002Fb\u003E。至于核心算法、我都只应用了标准库和 Numpy 来实现,不过由于神经网络部分稍微有些特殊,所以那部分我会分两块讲:\u003C\u002Fp\u003E\u003Col\u003E\u003Cli\u003E基于 tensorflow 的实现的说明。这一块会是主体,它能帮助我们快速了解一个大的框架\u003C\u002Fli\u003E\u003Cli\u003E基于 Numpy 的实现、也就(大概)是所谓的重新造轮子的说明。重新造轮子的意义更多在于\u003Cb\u003E理解算法细节\u003C\u002Fb\u003E和\u003Cb\u003E熟悉编程语言的各种运用\u003C\u002Fb\u003E,如果是应用在工程上的话可能还是使用成熟的框架(比如 tensorflow 啊、tensorflow 什么的)比较好。\n这一块的章节都是带星号的章节,个人的建议是在看这些章节之前、先把没带星号的章节看一遍;如果觉得不过瘾、想要了解更多细节的话、再来看相对应的带星号章节。所有带星号的章节的顶部都会有相应的不带星号章节的链接,观众老爷们如果没看过的话可以先戳进去看看 ( σ'ω')σ\u003C\u002Fli\u003E\u003C\u002Fol\u003E\u003Cp\u003E然后就是这一个系列主要说的是实现,原理的话会在以“数学”为前缀的系列里面说\u003C\u002Fp\u003E\u003Cp\u003E毕竟我还算是个数院的学生(躺\u003C\u002Fp\u003E\u003Cp\u003E虽然码代码的时间比做数学题的时间多(躺\u003C\u002Fp\u003E\u003Cp\u003E以上(用的括号可能有点多)(希望观众老爷们多多谅解)(逃\u003C\u002Fp\u003E\u003Cp\u003E(另:在下学习机器学习的时间说实话非常有限,很多地方可能理解的不准确甚至有错,届时恳请观众老爷们点出并批评,我会马上更正并注明师出何方~)\u003C\u002Fp\u003E&,&updated&:new Date(&T14:30:46.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:31,&likeCount&:64,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-f7ebb973bad_r.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&topics&:[{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&Python&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&机器学习&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&数学&}],&adminClosedComment&:false,&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&sourceUrl&:&&,&pageCommentsCount&:31,&snapshotUrl&:&&,&publishedTime&:&T22:30:46+08:00&,&url&:&\u002Fp\u002F&,&summary&:&==========
更新 ========== \n最近写了个 Tensorflow 的 RNN 封装,应该可以拿它来讲讲 RNN 了主要是感觉现在网上的相关资源都太不友好了,特别是 Tensorflow 官网那个……所以就干脆自己弄了个(个人认为)比较友好的封装\n==========
…&,&reviewingCommentsCount&:0,&meta&:{&previous&:null,&next&:null},&commentsCount&:31,&likesCount&:64},&&:{&title&:&Python · 朴素贝叶斯(一)· 框架&,&author&:&carefree0910&,&content&:&\u003Cp\u003E(这里是本章会用到的 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Fblob\u002Fmaster\u002Fb_NaiveBayes\u002FVectorized\u002FBasic.py\& data-editable=\&true\& data-title=\&GitHub\&\u003EGitHub\u003C\u002Fa\u003E 地址)\u003C\u002Fp\u003E\u003Cp\u003E(话说知乎居然没有朴素贝叶斯的话题……呜呼 (ノへ ̄、)) \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E========== 写在前面的话 ==========\u003C\u002Fp\u003E\u003Cp\u003E对于我个人而言、光看这么一个框架是非常容易摸不着头脑的\u003Cbr\u003E毕竟之前花了许多时间在数学部分讲的那些算法完全没有体现在这个框架中、取而代之的是一些我抽象出来的和算法无关的结构性部分……\u003Cbr\u003E虽然从逻辑上来说应该先说明如何搭建这个框架,但从容易理解的角度来说、个人建议先不看这章的内容而是先看后续的实现具体算法的章节\u003Cbr\u003E然后如果那时有不懂的定义、再对照这一章的相关部分来看\u003Cbr\u003E不过如果是对朴素贝叶斯算法非常熟悉的观众老爷的话、直接看本章的抽象会引起一些共鸣也说不定 ( σ'ω')σ\u003C\u002Fp\u003E\u003Cp\u003E========== 分割线的说 ==========\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E所谓的框架、自然是指三种朴素贝叶斯模型(离散、连续、混合)共性的抽象了。由于贝叶斯决策论就摆在那里、不难知道如下功能是通用的:\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E计算类别的先验概率\u003C\u002Fli\u003E\u003Cli\u003E训练出一个能输出后验概率的决策函数\u003C\u002Fli\u003E\u003Cli\u003E利用该决策函数进行预测和评估\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E虽说朴素贝叶斯大体上来说只是简单的计数、但是想以比较高的效率做好这件事却比想象中的要麻烦不少(说实话麻烦到我有些不想讲的程度了)(喂)\u003C\u002Fp\u003E\u003Cp\u003E总之先来看看这个框架的初始化步骤吧(前方……高能?!)\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eclass NaiveBayes(ClassifierBase, metaclass=ClassifierMeta):\n
初始化结构\n
self._x, self._y:记录训练集的变量\n
self._data:核心数组,存储实际使用的条件概率的相关信息\n
self._func:模型核心——决策函数,能够根据输入的x、y输出对应的后验概率\n
self._n_possibilities:记录各个维度特征取值个数的数组\n
self._labelled_x:记录按类别分开后的输入数据的数组\n
self._label_zip:记录类别相关信息的数组,视具体算法、定义会有所不同\n
self._cat_counter:核心数组,记录第i类数据的个数(cat是category的缩写)\n
self._con_counter:核心数组,用于记录数据条件概率的原始极大似然估计\n
self.label_dic:核心字典,用于记录数值化类别时的转换关系\n
self._feat_dics:核心字典,用于记录数值化各维度特征(feat)时的转换关系\n
def __init__(self):\n
self._x = self._y = None\n
self._data = self._func = None\n
self._n_possibilities = None\n
self._labelled_x = self._label_zip = None\n
self._cat_counter = self._con_counter = None\n
self.label_dic = self._feat_dics = None\n\u003C\u002Fcode\u003E\u003Cp\u003E其中、self._con_counter[d][c][p] =\u003Cequation\u003E\\hat p(x^{(d)}=p|y=c)\u003C\u002Fequation\u003E(con是conditional的缩写)\u003Cimg src=\&v2-5d2a12f1e02cfb.jpg\& data-rawwidth=\&640\& data-rawheight=\&377\&\u003E\u003C\u002Fp\u003E\u003Cp\u003E (注释比代码还多是想闹哪样???(╯‵□′)╯︵┻━┻)\u003C\u002Fp\u003E\u003Cp\u003E总之和我一样懵逼了的观众老爷们可以先不太在意这一坨是什么玩意儿,毕竟这些东西是抽象程度比较高的属性……等结合具体算法时、这些属性的意义可能就会明确得多\u003C\u002Fp\u003E\u003Cp\u003E下面进入正题……首先来看怎么计算先验概率(直接利用上面的 self._cat_counter属性即可)\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef get_prior_probability(self, lb=1):\n
return [(_c_num + lb) \u002F (len(self._y) + lb * len(self._cat_counter))\n
for _c_num in self._cat_counter]\u003C\u002Fcode\u003E\u003Cp\u003E其中参数 lb 即为平滑项,默认为 1 意味着默认使用拉普拉斯平滑 \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E然后看看训练步骤能如何进行抽象\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef fit(self, x=None, y=None, sample_weight=None, lb=1):\n
if x is not None and y is not None:\n
self.feed_data(x, y, sample_weight)\n
self._func = self._fit(lb)\n\u003C\u002Fcode\u003E\u003Cp\u003E(岂可修不就只是调用了一下 feed_data 方法而已嘛还说成抽象什么的行不行啊!)\u003C\u002Fp\u003E\u003Cp\u003E其中用到的 feed_data 方法是留给各个子类定义的、进行数据预处理的方法;然后 self._fit 可说是核心训练函数、它会返回我们的决策函数 self._func\u003C\u002Fp\u003E\u003Cp\u003E最后看看怎样利用 self._func 来预测未知数据\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef predict(self, x, get_raw_result=False):\n
# 调用相应方法进行数据预处理(这在离散型朴素贝叶斯中尤为重要)\n
x = self._transfer_x(x)\n
# 只有将算法进行向量化之后才能做以下的步骤\n
m_arg, m_probability = np.zeros(len(x), dtype=np.int8), np.zeros(len(x))\n
# len(self._cat_counter) 其实就是类别个数\n
for i in range(len(self._cat_counter)):\n
# 注意这里的 x 其实是矩阵、p 是对应的“后验概率矩阵”:p = p(y=i|x)\n
# 这意味着决策函数 self._func 需要支持矩阵运算\n
p = self._func(x, i)\n
# 利用 Numpy 进行向量化操作\n
_mask = p & m_probability\n
m_arg[_mask], m_probability[_mask] = i, p[_mask]\n
# 利用转换字典 self.label_dic 输出决策\n
# 参数 get_raw_result 控制该函数是输出预测的类别还是输出相应的后验概率\n
if not get_raw_result:\n
return np.array([self.label_dic[arg] for arg in m_arg])\n
return m_probability\u003C\u002Fcode\u003E\u003Cp\u003E其中 self.label_dic 大概是这个德性的:比如训练集的类别空间为 {red, green, blue} 然后第一个样本的类别是 red 且第二个样本的类别是 blue、那么就有\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eself.label_dic = np.array([\&red\&, \&blue\&, \&green\&])\n\u003C\u002Fcode\u003E\u003Cp\u003E以上就是朴素贝叶斯模型框架的搭建,下一章会在该框架的基础上实现离散型朴素贝叶斯模型\u003C\u002Fp\u003E\u003Cp\u003E希望观众老爷们能不讨厌ヽ(?ω??)?\u003C\u002Fp\u003E\u003Cp\u003E(\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F?refer=carefree0910-pyml\& class=\&\& data-editable=\&true\& data-title=\&猛戳我进入下一章! ( σ'ω')σ\&\u003E猛戳我进入下一章! ( σ'ω')σ\u003C\u002Fa\u003E )
\u003C\u002Fp\u003E&,&updated&:new Date(&T12:44:12.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:6,&likeCount&:35,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T20:44:12+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-de540bc74516eb_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:6,&likesCount&:35},&&:{&title&:&图像分类(零)&,&author&:&carefree0910&,&content&:&\u003Cp\u003E(知乎居然没有迁移学习的话题……呜呼 (ノへ ̄、))\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E本系列所介绍的小项目的 GitHub 地址:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Ftree\u002Fmaster\u002F_Dist\u002FImageRecognition\& data-editable=\&true\& data-title=\&“内嵌”版\&\u003E“内嵌”版\u003C\u002Fa\u003E(嵌在主 repo 中的版本)\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&https:\u002F\u002Fgithub.com\u002FcarefreeFImageRecognition\u002Ftree\u002Fmaster\& data-title=\&Stand-alone 版\& class=\&\& data-editable=\&true\&\u003EStand-alone 版\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E本系列主要介绍一下如何利用 Google (是 Google 吧?)的 Inception v3 来解决图像分类任务。虽说我们之前说过许多算法了、但总免不了有种纸上谈兵的感觉,这一系列中我们就来说说如何利用机器学习来解决(大概算是)现实中的问题\u003C\u002Fp\u003E\u003Cp\u003E(其实该小项目仅仅起由于我的一个作业而已科科)(喂) \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E本章作为第零章、主要展示一下该小项目的使用方法,开头两个 GitHub 地址中附带的 README 虽然已经相当详细、但没图的话读起来可能还是会犯懒,于是在这我们来个图文解说如何跑起这个小项目吧:\u003C\u002Fp\u003E\u003Cp\u003E1、下载 \u003Ca href=\&https:\u002F\u002Fstorage.googleapis.com\u002Fdownload.tensorflow.org\u002Fmodels\u002Finception_dec_2015.zip\& data-editable=\&true\& data-title=\&Inception-v3\& class=\&\&\u003EInception-v3\u003C\u002Fa\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E2、下载 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002FcarefreeFImageRecognition\u002Ftree\u002Fmaster\& data-title=\&Stand-alone 版本\& class=\&\& data-editable=\&true\&\u003EStand-alone 版本\u003C\u002Fa\u003E的 zip,解压至任一地方 \u003C\u002Fp\u003E\u003Cp\u003E3、把 Inception-v3 模型对应的 tensorflow_inception_graph.pb 重命名为 Model.pb 后、扔进 Models\u002FExtractors\u002Fv3 文件夹中,操作完后的相应文件夹应如下图所示:\u003C\u002Fp\u003E\u003Cimg src=\&v2-97ac266b7cddfa13e5334d6.png\& data-rawwidth=\&1177\& data-rawheight=\&710\&\u003E\u003Cp\u003E4、准备自己的图像数据集(用文件夹分好类,文件夹名即为类名;注意为了更好地进行可视化、需要将名字弄成英文的【因为 cv2 不支持中文!岂可修!】)、或者下载\u003Ca href=\&http:\u002F\u002Fpan.baidu.com\u002Fs\u002F1bQGSKQ\& data-editable=\&true\& data-title=\&这里\&\u003E这里\u003C\u002Fa\u003E的数据集\u003C\u002Fp\u003E\u003Cp\u003E5、把数据集扔进 _Data 文件夹中,如下图:\u003C\u002Fp\u003E\u003Cp\u003E\u003Cimg src=\&v2-039b2fac8f708a0c71a0a3c3e192bd7b.png\& data-rawwidth=\&1441\& data-rawheight=\&723\&\u003E6、双击 Main.py。注意此时程序会把 _Data 中的 200 张图片\u003Cb\u003E剪切\u003C\u002Fb\u003E到 Test 文件夹中来当测试集,如果不想训练集被剪的话、可以随便扔几张\u003Cb\u003E图片\u003C\u002Fb\u003E到 Test 文件夹中\u003C\u002Fp\u003E\u003Cp\u003E然后程序就会愉快地跑起来、这时观众老爷们可以去喝杯咖啡……最后程序跑完之后、一个图像分类模型就会搭建完毕。然后为了直观认知模型性能、我用 cv2 写了一个(比较简陋)的可视化,它会生成如下图所示的概览图:\u003C\u002Fp\u003E\u003Cimg src=\&v2-f65f87d03ed4ffac26f8811.png\& data-rawwidth=\&1799\& data-rawheight=\&763\&\u003E\u003Cp\u003E(哦呼……好丑……) \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E其中绿得越鲜艳说明越对、红得越鲜艳说明越错;那些比较暗淡但是偏绿的就是不太确信、然而判断对了的,那些暗淡偏红的就是不太确信并错掉的\u003C\u002Fp\u003E\u003Cp\u003E通过双击相应的缩略图、可以查看模型表现的详情。比如假设我想看看最右边那两个暗淡的玩意儿到底分对了没有:\u003C\u002Fp\u003E\u003Cimg src=\&v2-c5dac9fc5e746dba28bc2.png\& data-rawwidth=\&1803\& data-rawheight=\&762\&\u003E\u003Cp\u003E可以看到左边那个是暗淡偏绿、亦即虽然对了却不太确信,右边那个是暗淡偏红、亦即虽然错了却也没错太多。可以看到多少也算情有可原…… \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E(不,我其实并不能分清暗淡的那些到底是偏绿还是偏红。我是通过左上角来判断的:对了的话第一个词是 Detail、后面跟一个确信度(确信概率);错了的话则是 True label 并会在后面告诉你正确的类是什么、同时会跟一个正确类的确信度) \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E与此同时、在 _Data\u002F_Cache 文件夹中会躺着三个 numpy 数组:\u003C\u002Fp\u003E\u003Cimg src=\&v2-c1fa722c782f2ccb509e987.png\& data-rawwidth=\&1180\& data-rawheight=\&712\&\u003E\u003Cp\u003E其中大写的 LABEL_DIC 记录着每个类别的类别名,features、labels 则是用 Inception-v3 提取出来的特征和对应的类别,有需要的观众老爷可以拿自己的分类器去对它分下类(我使用的是 NN)\u003C\u002Fp\u003E\u003Cp\u003E对于新的需要进行分类的图片、把它们全扔进 Test 文件夹中后再运行 Main.py 即可\u003C\u002Fp\u003E\u003Cp\u003E从下一章开始我会说明一下怎么实现这个小项目;不过说是实现、其实绝大多数的功能都是由 Inception-v3 保证的……我本人只做了一些非常微小的工作(喂)\u003C\u002Fp\u003E\u003Cp\u003E希望观众老爷们能够喜欢~\u003C\u002Fp\u003E&,&updated&:new Date(&T11:30:50.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:13,&likeCount&:12,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T19:30:50+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-9f5b0cc14eda990cbf7dfd07_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:13,&likesCount&:12},&&:{&title&:&Python · 朴素贝叶斯(二)· MultinomialNB&,&author&:&carefree0910&,&content&:&\u003Cp\u003E(这里是本章会用到的 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Fblob\u002Fmaster\u002Fb_NaiveBayes\u002FVectorized\u002FMultinomialNB.py\& class=\&\& data-editable=\&true\& data-title=\&GitHub\&\u003EGitHub\u003C\u002Fa\u003E 地址)\u003C\u002Fp\u003E\u003Cp\u003E本章主要介绍离散型朴素贝叶斯—— MultinomialNB 的实现。对于离散型朴素贝叶斯模型的实现,由于核心算法都是在进行“计数”工作、所以问题的关键就转换为了如何进行计数。幸运的是、Numpy 中的一个方法:bincount 就是专门用来计数的,它能够非常快速地数出一个数组中各个数字出现的频率;而且由于它是 Numpy 自带的方法,其速度比 Python 标准库 collections 中的计数器 Counter 还要快上非常多。不幸的是、该方法有如下两个缺点:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E 只能处理非负整数型中数组\u003C\u002Fli\u003E\u003Cli\u003E向量中的最大值即为返回的数组的长度,换句话说,如果用 bincount 方法对一个长度为 1、元素为 1000 的数组计数的话,返回的结果就是 999 个 0 加 1 个 1 \u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E所以我们做数据预处理时就要充分考虑到这两点。我们之前曾经在\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&这里\& class=\&\&\u003E这篇文章\u003C\u002Fa\u003E的最后说明了如何将数据进行数值化,该数据数值化的方法其实可以说是为 bincount 方法“量身定做”的。举个栗子,当原始数据形如:\u003C\u002Fp\u003E\u003Cblockquote\u003E\u003Cp\u003Ex, s, n, t, p, f\u003Cbr\u003Ex, s, y, t, a, f\u003Cbr\u003Eb, s, w, t, l, f\u003Cbr\u003Ex, y, w, t, p, f\u003Cbr\u003Ex, s, g, f, n, f\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E时,调用上述数值化数据的方法将会把数据数值化为:\u003C\u002Fp\u003E\u003Cblockquote\u003E\u003Cp\u003E0, 0, 0, 0, 0, 0\u003Cbr\u003E0, 0, 1, 0, 1, 0\u003Cbr\u003E1, 0, 2, 0, 2, 0\u003Cbr\u003E0, 1, 2, 0, 0, 0\u003Cbr\u003E0, 0, 3, 1, 3, 0\u003Cbr\u003E\u003C\u002Fp\u003E\u003C\u002Fblockquote\u003E\u003Cp\u003E单就实现这个功能而言、实现是平凡的:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef quantize_data(x):\n
features = [set(feat) for feat in xt]\n
feat_dics = [{\n
_l: i for i, _l in enumerate(feats)\n
} if not wc[i] else None for i, feats in enumerate(features)]\n
x = np.array([[\n
feat_dics[i][_l] for i, _l in enumerate(sample)]\n
for sample in x])\n
return x, feat_dics\n\u003C\u002Fcode\u003E\u003Cp\u003E不过考虑到离散型朴素贝叶斯需要的东西比这要多很多,所以才有了\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-title=\&这里\& class=\&\& data-editable=\&true\&\u003E这里\u003C\u002Fa\u003E最后所介绍的、相对而言繁复很多的版本。建议观众老爷们在看接下来的实现之前先把那个 quantize_data 函数的完整版看一遍、因为我接下来会直接用……(那你很棒棒哦)\u003C\u002Fp\u003E\u003Cp\u003E当然考虑到朴素贝叶斯的相关说明已经远在一个月以前了(你以为是谁的错啊喂)、我就不把实现一股脑扔出来了,那样估计所有人(包括我自己在内)都看不懂……所以我决定把离散型朴素贝叶斯算法和对应的实现进行逐一讲解 ( σ'ω')σ\u003C\u002Fp\u003E\u003Ch2\u003E计算先验概率\u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E 这倒是在上一章已经讲过了、但我还是打算重讲一遍……首先把实现放出来:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef get_prior_probability(self, lb=1):\n
return [(_c_num + lb) \u002F (len(self._y) + lb * len(self._cat_counter))\n
for _c_num in self._cat_counter]\n\u003C\u002Fcode\u003E\u003Cp\u003E其中的 lb 为平滑系数(默认为 1、亦即拉普拉斯平滑),这对应的公式其实是带平滑项的、先验概率的极大似然估计:\u003Cimg src=\&v2-aef46c167e8b7845c8cee2a.png\& data-rawwidth=\&362\& data-rawheight=\&60\&\u003E(什么?你说我以前没讲过平滑?一定是你的错觉!不信看\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F?refer=carefree0910-pyml\& data-title=\&这里\& class=\&\& data-editable=\&true\&\u003E这里\u003C\u002Fa\u003E!我明明今天更新了!)(喂\u003C\u002Fp\u003E\u003Cp\u003E所以代码中的 self._cat_counter 的意义就很明确了——它存储着 K 个\u003Cequation\u003E\\sum_{i=1}^NI(y_i=c_k)\u003C\u002Fequation\u003E\u003C\u002Fp\u003E\u003Cp\u003E(cat counter 其实是 category counter 的简称……我知道我命名很差所以不要打我……)\u003C\u002Fp\u003E\u003Ch2\u003E计算条件概率\u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E同样先看核心实现:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edata = [[] for _ in range(n_dim)]\nfor dim, n_possibilities in enumerate(self._n_possibilities):\n
data[dim] = [[\n
(self._con_counter[dim][c][p] + lb) \u002F (\n
self._cat_counter[c] + lb * n_possibilities)\n
for p in range(n_possibilities)] for c in range(n_category)]\nself._data = [np.array(dim_info) for dim_info in data]\n\u003C\u002Fcode\u003E\u003Cp\u003E这对应的公式其实就是带平滑项(lb)的条件概率的极大似然估计:\u003Cimg src=\&v2-78b19a12ddb2b.png\& data-rawwidth=\&400\& data-rawheight=\&61\&\u003E其中\u003Cimg src=\&v2-3ad22f51acd2df2332f60.png\& data-rawwidth=\&276\& data-rawheight=\&36\&\u003E\u003C\u002Fp\u003E\u003Cbr\u003E\u003Cp\u003E可以看到我们利用到了 self._cat_counter 属性来计算\u003Cequation\u003E\\sum_{i=1}^NI(y_i=c_k)\u003C\u002Fequation\u003E。同时可以看出:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003En_category 即为 K \u003Cbr\u003E\u003C\u002Fli\u003E\u003Cli\u003Eself._n_possibilities
储存着 n 个\u003Cequation\u003ES_j\u003C\u002Fequation\u003E\u003Cbr\u003E\u003C\u002Fli\u003E\u003Cli\u003Eself._con_counter 储存的即是各个\u003Cequation\u003E\\sum_{i=1}^NI(x_i^{(j)}=a_{jl}, y_i=c_k)\u003C\u002Fequation\u003E的值。具体而言:\u003Cimg src=\&v2-e8c91de9ab1d727f991b69.png\& data-rawwidth=\&352\& data-rawheight=\&36\&\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E至于 self._data、就只是为了向量化算法而存在的一个变量而已,它将 data 中的每一个列表都转成了 Numpy 数组、以便在计算后验概率时利用 Numpy 数组的 Fancy Indexing 来加速算法\u003C\u002Fp\u003E\u003Cp\u003E聪明的观众老爷可能已经发现、其实 self._con_counter 才是计算条件概率的关键,事实上这里也正是 bincount 大放异彩的地方。以下为计算 self._con_counter 的函数的实现:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef feed_sample_weight(self, sample_weight=None):\n
self._con_counter = []\n
for dim, _p in enumerate(self._n_possibilities):\n
if sample_weight is None:\n
self._con_counter.append([\n
np.bincount(xx[dim], minlength=_p) for xx in\n
self._labelled_x])\n
self._con_counter.append([\n
np.bincount(xx[dim], weights=sample_weight[\n
label] \u002F sample_weight[label].mean(), minlength=_p)\n
for label, xx in self._label_zip])\n\u003C\u002Fcode\u003E\u003Cp\u003E可以看到、bincount 方法甚至能帮我们处理样本权重的问题。\u003C\u002Fp\u003E\u003Cp\u003E代码中有两个我们还没进行说明的属性:self._labelled_x 和 self._label_zip,不过从代码上下文不难推断出、它们储存的是应该是不同类别所对应的数据。具体而言:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003Eself._labelled_x:记录按类别分开后的、输入数据的数组\u003C\u002Fli\u003E\u003Cli\u003Eself._label_zip:比 self._labelled_x 多记录了个各个类别的数据所对应的下标\u003Cbr\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E这里就提前将它们的实现放出来以帮助理解吧:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003E# 获得各类别数据的下标\nlabels = [y == value for value in range(len(cat_counter))]\n# 利用下标获取记录按类别分开后的输入数据的数组\nlabelled_x = [x[ci].T for ci in labels]\nself._labelled_x, self._label_zip = labelled_x, list(zip(labels, labelled_x))\n\u003C\u002Fcode\u003E\u003Ch2\u003E计算后验概率\u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E仍然先看核心实现:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef func(input_x, tar_category):\n
input_x = np.atleast_2d(input_x).T\n
rs = np.ones(input_x.shape[1])\n
for d, xx in enumerate(input_x):\n
rs *= self._data[d][tar_category][xx]\n
return rs * p_category[tar_category]\n\u003C\u002Fcode\u003E\u003Cp\u003E 这对应的公式其实就是决策公式:\u003Cimg src=\&v2-67f597accec28a9d02143.png\& data-rawwidth=\&400\& data-rawheight=\&57\&\u003E\u003C\u002Fp\u003E\u003Cbr\u003E\u003Cp\u003E所以不难看出代码中的 p_category 存储着 K 个\u003Cequation\u003Ep(y=c_k)\u003C\u002Fequation\u003E\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch2\u003E封装\u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E最后要做的、无非就是把上述三个步骤进行封装而已,首先是数据预处理:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef feed_data(self, x, y, sample_weight=None):\n
if sample_weight is not None:\n
sample_weight = np.array(sample_weight)\n
# 调用 quantize_data 函数获得诸多信息\n
x, y, _, features, feat_dics, label_dic = DataUtil.quantize_data(\n
x, y, wc=np.array([False] * len(x[0])))\n
# 利用 bincount 函数直接获得 self._cat_counter\n
cat_counter = np.bincount(y)\n
# 利用 features 变量获取各个维度的特征个数 Sj\n
n_possibilities = [len(feats) for feats in features]\n
# 获得各类别数据的下标\n
labels = [y == value for value in range(len(cat_counter))]\n
# 利用下标获取记录按类别分开后的输入数据的数组\n
labelled_x = [x[ci].T for ci in labels]\n
# 更新模型的各个属性\n
self._x, self._y = x, y\n
self._labelled_x, self._label_zip = labelled_x, list(\n
zip(labels, labelled_x))\n
self._cat_counter, self._feat_dics, self._n_possibilities = cat_counter, feat_dics, n_possibilities\n
self.label_dic = label_dic\n
self.feed_sample_weight(sample_weight)\n\u003C\u002Fcode\u003E\u003Cp\u003E然后利用\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-title=\&上一章\& class=\&\& data-editable=\&true\&\u003E上一章\u003C\u002Fa\u003E我们定义的框架的话、只需定义核心训练函数即可:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef _fit(self, lb):\n
n_dim = len(self._n_possibilities)\n
n_category = len(self._cat_counter)\n
p_category = self.get_prior_probability(lb)\n\n
data = [[] for _ in range(n_dim)]\n
for dim, n_possibilities in enumerate(self._n_possibilities):\n
data[dim] = [[\n
(self._con_counter[dim][c][p] + lb) \u002F (\n
self._cat_counter[c] + lb * n_possibilities)\n
for p in range(n_possibilities)] for c in range(n_category)]\n
self._data = [np.array(dim_info) for dim_info in data]\n\n
def func(input_x, tar_category):\n
input_x = np.atleast_2d(input_x).T\n
rs = np.ones(input_x.shape[1])\n
for d, xx in enumerate(input_x):\n
rs *= self._data[d][tar_category][xx]\n
return rs * p_category[tar_category]\n
return func\n\u003C\u002Fcode\u003E\u003Cp\u003E最后,我们需要定义一个将测试数据转化为模型所需的、数值化数据的方法:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Edef _transfer_x(self, x):\n
for i, sample in enumerate(x):\n
for j, char in enumerate(sample):\n
x[i][j] = self._feat_dics[j][char]\n
return x\n\u003C\u002Fcode\u003E\u003Cp\u003E至此,离散型朴素贝叶斯就全部实现完毕了(鼓掌!)\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ch2\u003E评估与可视化\u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E可以拿 UCI 上一个比较出名(简单)的“蘑菇数据集(Mushroom\nData Set)”来评估一下我们的模型。该数据集的大致描述如下:它有 8124 个样本、22 个属性,类别取值有两个:“能吃”或“有毒”;该数据每个单一样本都占一行、属性之间使用逗号隔开。选择该数据集的原因是它无需进行额外的数据预处理、样本量和属性量都相对合适、二类分类问题也相对来说具有代表性。更重要的是,它所有维度的特征取值都是离散的、从而非常适合用来测试我们的MultinomialNB 模型。\u003C\u002Fp\u003E\u003Cp\u003E完整的数据集可以参见\u003Ca href=\&https:\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Fblob\u002Fmaster\u002F_Data\u002Fmushroom.txt\& data-editable=\&true\& data-title=\&https:\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Fblob\u002Fmaster\u002F\n_Data\u002Fmushroom.txt\& class=\&\&\u003E这里\u003C\u002Fa\u003E(第一列数据是类别),我们的模型在其上的表现如下图所示:\u003Cimg src=\&v2-98e074bd12a1fda653fec2e12a9e16d7.png\& data-rawwidth=\&387\& data-rawheight=\&144\&\u003E\u003C\u002Fp\u003E\u003Cp\u003E 其中第一、二行分别是训练集、测试集上的准确率,接下来三行则分别是建立模型、评估模型和总花费时间的记录\u003C\u002Fp\u003E\u003Cp\u003E当然,仅仅看一个结果没有什么意思、也完全无法知道模型到底干了什么。为了获得更好的直观,我们可以进行一定的可视化,比如说将极大似然估计法得到的条件概率画出(如第零章所示的那样)。可视化的代码实现如下:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003E# 导入 matplotlib 库以进行可视化\nimport matplotlib.pyplot as plt\n# 进行一些设置使得 matplotlib 能够显示中文\nfrom pylab import mpl\n# 将字体设为“仿宋”\nmpl.rcParams['font.sans-serif'] = ['FangSong']\nmpl.rcParams['axes.unicode_minus'] = False\n# 利用 MultinomialNB 搭建过程中记录的变量获取条件概率\ndata = nb[\&data\&]\n# 定义颜色字典,将类别 e(能吃)设为天蓝色、类别 p(有毒)设为橙色\ncolors = {\&e\&: \&lightSkyBlue\&, \&p\&: \&orange\&}\n# 利用转换字典定义其“反字典”,后面可视化会用上\n_rev_feat_dics = [{_val: _key for _key, _val in _feat_dic.items()} \n
for _feat_dic in self._feat_dics]\n# 遍历各维度进行可视化\n# 利用 MultinomialNB 搭建过程中记录的变量,获取画图所需的信息\nfor _j in range(nb[\&x\&].shape[1]):\n
sj = nb[\&n_possibilities\&][_j]\n
tmp_x = np.arange(1, sj+1)\n
# 利用 matplotlib 对 LaTeX 的支持来写标题,两个 $ 之间的即是 LaTeX 语句\n
title = \&$j = {}; S_j = {}$\&.format(_j+1, sj)\n
plt.figure()\n
plt.title(title)\n
# 根据条件概率的大小画出柱状图\n
for _c in range(len(nb.label_dic)):\n
plt.bar(tmp_x-0.35*_c, data[_j][_c, :], width=0.35,\n
facecolor=colors[nb.label_dic[_c]], edgecolor=\&white\&, \n
label=\&class: {}\&.format(nb.label_dic[_c]))\n
# 利用上文定义的“反字典”将横坐标转换成特征的各个取值\n
plt.xticks([i for i in range(sj + 2)], [\&\&] + [_rev_dic[i] for i in range(sj)] + [\&\&])\n
plt.ylim(0, 1.0)\n
plt.legend()\n
# 保存画好的图像\n
plt.savefig(\&d{}\&.format(j+1))\n\u003C\u002Fcode\u003E\u003Cp\u003E由于蘑菇数据一共有 22 维,所以上述代码会生成 22 张图,从这些图可以非常清晰地看出训练数据集各维度特征的分布。下选出几组有代表性的图片进行说明。\u003Cbr\u003E一般来说,一组数据特征中会有相对“重要”的特征和相对“无足轻重”的特征,通过以上实现的可视化可以比较轻松地辨析出在离散型朴素贝叶斯中这两者的区别。比如说,在离散型朴素贝叶斯里、相对重要的特征的表现会如下图所示(左图对应第 5 维、右图对应第 19 维):\u003C\u002Fp\u003E\u003Cimg src=\&v2-a60cedad56be5.png\& data-rawwidth=\&1390\& data-rawheight=\&566\&\u003E\u003Cp\u003E可以看出,蘑菇数据集在第 19 维上两个类别各自的“优势特征”都非常明显、第 5 维上两个类别各自特征的取值更是基本没有交集。可以想象,即使只根据第 5 维的取值来进行类别的判定、最后的准确率也一定会非常高\u003C\u002Fp\u003E\u003Cp\u003E\n\n那么与之相反的、在 MultinomialNB 中相对没那么重要的特征的表现则会形如下图所示(左图对应第 3 维、右图对应第 16 维):\u003C\u002Fp\u003E\u003Cp\u003E\u003Cimg src=\&v2-b324844bedeadcec62e523.png\& data-rawwidth=\&1374\& data-rawheight=\&568\&\u003E\u003Cbr\u003E可以看出,蘑菇数据集在第 3 维上两个类的特征取值基本没有什么差异、第 16 维数据更是似乎完全没有存在的价值。像这样的数据就可以考虑直接剔除掉\u003C\u002Fp\u003E\u003Ch2\u003E总结 \u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E……感觉没啥好总结的了(趴) \u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E看到这里的观众老爷如果再回过头去看\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&上一章\&\u003E上一章\u003C\u002Fa\u003E所讲的框架、想必会有些新的体会吧 ( σ'ω')σ\u003C\u002Fp\u003E\u003Cp\u003E希望观众老爷们能够喜欢~\u003C\u002Fp\u003E\u003Cp\u003E(\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F?refer=carefree0910-pyml\& class=\&\& data-editable=\&true\& data-title=\&猛戳我进入下一章! ( σ'ω')σ \&\u003E猛戳我进入下一章! ( σ'ω')σ \u003C\u002Fa\u003E)\u003C\u002Fp\u003E&,&updated&:new Date(&T15:56:02.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:4,&likeCount&:24,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T23:56:02+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-ada62f3f5e1ccd0a6e2e3f_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:4,&likesCount&:24},&&:{&title&:&Python · 朴素贝叶斯(三)· GaussianNB&,&author&:&carefree0910&,&content&:&\u003Cp\u003E(这里是本章会用到的 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Fblob\u002Fmaster\u002Fb_NaiveBayes\u002FVectorized\u002FGaussianNB.py\& class=\&\& data-editable=\&true\& data-title=\&GitHub\&\u003EGitHub\u003C\u002Fa\u003E 地址)\u003C\u002Fp\u003E\u003Cp\u003E(话说居然一个月没写了啊……)(然而你一个月没写之后写的东西还这么水对得起观众老爷吗)\u003C\u002Fp\u003E\u003Cp\u003E(刚发现算法叙述居然在\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& class=\&\& data-editable=\&true\& data-title=\&这篇文章\&\u003E这篇文章\u003C\u002Fa\u003E中说过了…… ( σ'ω')σ)\u003C\u002Fp\u003E\u003Cp\u003E本章主要介绍连续型朴素贝叶斯—— GaussianNB 的实现。在有了实现离散型朴素贝叶斯的经验后,实现连续型朴素贝叶斯模型其实只是个触类旁通的活了。算法的叙述已经在\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& class=\&\& data-editable=\&true\& data-title=\&这篇文章\&\u003E这篇文章\u003C\u002Fa\u003E中进行过说明,下面就直接看看如何进行实现\u003C\u002Fp\u003E\u003Cp\u003E由 GaussianNB 的算法可知,在实现 GaussianNB 之前、我们需要先实现一个能够计算正态分布密度和进行正态分布极大似然估计的类:\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eimport numpy as np\nfrom math import pi, exp\n\n# 记录常量以避免重复运算\nsqrt_pi = (2 * pi) ** 0.5\n\nclass NBFunctions:\n
# 定义正态分布的密度函数\n
@staticmethod\n
def gaussian(x, mu, sigma):\n\treturn np.exp(\n
-(x - mu) ** 2 \u002F (2 * sigma)) \u002F (sqrt_pi * sigma ** 0.5)\n\n
# 定义进行极大似然估计的函数\n
# 它能返回一个存储着计算条件概率密度的函数的列表\n
@staticmethod\n
def gaussian_maximum_likelihood(labelled_x, n_category, dim):\n
mu = [np.sum(\n
labelled_x[c][dim]) \u002F \n
len(labelled_x[c][dim]) for c in range(n_category)]\n
sigma = [np.sum(\n
(labelled_x[c][dim]-mu[c])**2) \u002F \n
len(labelled_x[c][dim]) for c in range(n_category)]\n
# 利用极大似然估计得到的和、定义生成计算条件概率密度的函数的函数 func\n
def func(_c):\n
def sub(xx):\n
return NBFunctions.gaussian(xx, mu[_c], sigma[_c])\n
return sub\n
# 利用 func 返回目标列表\n
return [func(_c=c) for c in range(n_category)]\n\u003C\u002Fcode\u003E\u003Cp\u003E\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n对于 GaussianNB 本身,由于算法中只有条件概率相关的定义变了、所以只需要将相关的函数重新定义即可。此外,由于输入数据肯定是数值数据、所以数据预处理会简单不少(至少不用因为要对输入进行特殊的数值化处理而记录其转换字典了)。考虑到上一章说明 MultinomialNB 的实现时已经基本把我们框架的思想都说明清楚了,在接下来的 GaussianNB 的代码实现中、我们会适当地减少注释以提高阅读流畅度(其实主要还是为了偷懒)(喂):\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Efrom b_NaiveBayes.Original.Basic import *\n\nclass GaussianNB(NaiveBayes):\n
def feed_data(self, x, y, sample_weight=None):\n
# 简单地调用 Python 自带的 float 方法将输入数据数值化\n
x = np.array([list(map(\n
lambda c: float(c), sample)) for sample in x])\n
# 数值化类别向量\n
labels = list(set(y))\n
label_dic = {label: i for i, label in enumerate(labels)}\n
y = np.array([label_dic[yy] for yy in y])\n
cat_counter = np.bincount(y)\n
labels = [y == value for value in range(len(cat_counter))]\n
labelled_x = [x[label].T for label in labels]\n
# 更新模型的各个属性\n
self._x, self._y = x.T, y\n
self._labelled_x, self._label_zip = labelled_x, labels\n
self._cat_counter, self.label_dic = (\n
cat_counter, {i: _l for _l, i in label_dic.items()}\n
self.feed_sample_weight(sample_weight)\n\u003C\u002Fcode\u003E\u003Cp\u003E\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n可以看到,数据预处理这一步确实要轻松很多。接下来只需要再定义训练用的代码就行,它们和 MultinomialNB 中的实现也大同小异:
\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003E
# 定义处理样本权重的函数\n
def feed_sample_weight(self, sample_weight=None):\n
if sample_weight is not None:\n
local_weights = sample_weight * len(sample_weight)\n
for i, label in enumerate(self._label_zip):\n
self._labelled_x[i] *= local_weights[label]\n\n
def _fit(self, lb):\n
n_category = len(self._cat_counter)\n
p_category = self.get_prior_probability(lb)\n
# 利用极大似然估计获得计算条件概率的函数、使用数组变量 data 进行存储\n
data = [\n
NBFunctions.gaussian_maximum_likelihood(\n
self._labelled_x, n_category, dim)\n
for dim in range(len(self._x))]\n
self._data = data\n
def func(input_x, tar_category):\n
# 将输入转换成二维数组(矩阵)\n
input_x = np.atleast_2d(input_x).T\n
rs = np.ones(input_x.shape[1])\n
for d, xx in enumerate(input_x):\n
rs *= data[d][tar_category](xx)\n
return rs * p_category[tar_category]\n
# 由于数据本身就是数值的,所以数据转换函数只需直接返回输入值即可\n
@staticmethod\n
def _transfer_x(x):\n
return x\n\u003C\u002Fcode\u003E\u003Cp\u003E\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n至此,连续型朴素贝叶斯模型就搭建完毕了\u003C\u002Fp\u003E\u003Cp\u003E\n\n连续型朴素贝叶斯同样能够进行和离散型朴素贝叶斯类似的可视化,不过由于我们接下来就要实现适用范围最广的朴素贝叶斯模型:混合型朴素贝叶斯了,所以我们这里不打算进行 GaussianNB 合理的评估、而打算把它归结到对混合型朴素贝叶斯的评估中\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E希望观众老爷们能够喜欢~\u003C\u002Fp\u003E\u003Cp\u003E(\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F?refer=carefree0910-pyml\& class=\&\& data-editable=\&true\& data-title=\&猛戳我进入下一章! ( σ'ω')σ \&\u003E猛戳我进入下一章! ( σ'ω')σ \u003C\u002Fa\u003E)\u003C\u002Fp\u003E&,&updated&:new Date(&T08:04:05.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:4,&likeCount&:17,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T16:04:05+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-6d0d8cce1b4e93e7d269_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:4,&likesCount&:17},&&:{&title&:&Python · 朴素贝叶斯(四)· MergedNB&,&author&:&carefree0910&,&content&:&(这里是本章会用到的 \u003Ca href=\&https:\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Fblob\u002Fmaster\u002Fb_NaiveBayes\u002FVectorized\u002FMergedNB.py\& class=\&\& data-editable=\&true\& data-title=\&GitHub\&\u003EGitHub\u003C\u002Fa\u003E 地址)\u003Cp\u003E(刚发现推广已经在\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&这篇文章\&\u003E这篇文章\u003C\u002Fa\u003E中说过了……那么本章其实就已经是朴素贝叶斯系列的最后一章了 ( σ'ω')σ)\u003C\u002Fp\u003E\u003Cp\u003E(刚发现算法叙述居然也说过了…… ( σ'ω')σ)\u003C\u002Fp\u003E\u003Cbr\u003E\u003Cp\u003E本章主要介绍混合型朴素贝叶斯—— MergedNB 的实现。算法的叙述已经在\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&这篇文章\&\u003E这篇文章\u003C\u002Fa\u003E中进行过说明,下面就直接看看如何进行实现。首先是初始化:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Efrom b_NaiveBayes.Original.Basic import *\nfrom b_NaiveBayes.Original.MultinomialNB import MultinomialNB\nfrom b_NaiveBayes.Original.GaussianNB import GaussianNB\n\nclass MergedNB(NaiveBayes):\n
初始化结构\n
self._whether_discrete:记录各个维度的变量是否是离散型变量\n
self._whether_continuous:记录各个维度的变量是否是连续型变量\n
self._multinomial、self._gaussian:离散型、连续型朴素贝叶斯模型\n
def __init__(self, whether_continuous):\n
self._multinomial, self._gaussian = (\n
MultinomialNB(), GaussianNB()\n
if whether_continuous is None:\n
self._whether_discrete = self._whether_continuous = None\n
self._whether_continuous = np.array(whether_continuous)\n
self._whether_discrete = ~self._whether_continuous\n\u003C\u002Fcode\u003E\u003Cp\u003E\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n接下来放出和模型的训练相关的实现,这一块将会大量重用之前在 MultinomialNB 和 GaussianNB 里面写过的东西:\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003E
def feed_data(self, x, y, sample_weight=None):\n
if sample_weight is not None:\n
sample_weight = np.array(sample_weight)\n
x, y, wc, features, feat_dics, label_dic = DataUtil.quantize_data(\n
x, y, wc=self._whether_continuous, separate=True)\n
# 若没有指定哪些维度连续,则用 quantize_data 中朴素的方法判定哪些维度连续\n
if self._whether_continuous is None:\n
self._whether_continuous = wc\n
# 通过Numpy中对逻辑非的支持进行快速运算\n
self._whether_discrete = ~self._whether_continuous\n
# 计算通用变量\n
self.label_dic = label_dic\n
discrete_x, continuous_x = x\n
cat_counter = np.bincount(y)\n
self._cat_counter = cat_counter\n
labels = [y == value for value in range(len(cat_counter))]\n
# 训练离散型朴素贝叶斯\n
labelled_x = [discrete_x[ci].T for ci in labels]\n
self._multinomial._x, self._multinomial._y = x, y\n
self._multinomial._labelled_x, self._multinomial._label_zip = (\n
labelled_x, list(zip(labels, labelled_x)))\n
self._multinomial._cat_counter = cat_counter\n
self._multinomial._feat_dics = [_dic\n
for i, _dic in enumerate(feat_dics) if self._whether_discrete[i]]\n
self._multinomial._n_possibilities = [len(feats)\n
for i, feats in enumerate(features) if self._whether_discrete[i]]\n
self._multinomial.label_dic = label_dic\n
# 训练连续型朴素贝叶斯\n
labelled_x = [continuous_x[label].T for label in labels]\n
self._gaussian._x, self._gaussian._y = continuous_x.T, y\n
self._gaussian._labelled_x, self._gaussian._label_zip = labelled_x, labels\n
self._gaussian._cat_counter, self._gaussian.label_dic = cat_counter, label_dic\n
# 处理样本权重\n
self._feed_sample_weight(sample_weight)\n\n
# 分别利用 MultinomialNB 和 GaussianNB 处理样本权重的方法来处理样本权重\n
def feed_sample_weight(self, sample_weight=None):\n
self._multinomial.feed_sample_weight(sample_weight)\n
self._gaussian.feed_sample_weight(sample_weight)\n\n
# 分别利用 MultinomialNB 和 GaussianNB 的训练函数来进行训练\n
def _fit(self, lb):\n
self._multinomial.fit()\n
self._gaussian.fit()\n
p_category = self._multinomial.get_prior_probability(lb)\n
discrete_func, continuous_func = (\n
self._multinomial[\&func\&], self._gaussian[\&func\&])\n
# 将 MultinomialNB 和 GaussianNB 的决策函数直接合成最终决策函数\n
# 由于这两个决策函数都乘了先验概率、我们需要除掉一个先验概率\n
def func(input_x, tar_category):\n
input_x = np.array(input_x)\n
return discrete_func(\n
input_x[self._whether_discrete].astype(\n
np.int), tar_category) * continuous_func(\n
input_x[self._whether_continuous], tar_category) \u002F p_category[tar_category]\n
return func\n\u003C\u002Fcode\u003E\u003Cp\u003E(又臭又长啊喂……)\u003C\u002Fp\u003E\u003Cp\u003E上述实现有一个显而易见的可以优化的地方:我们一共在代码中重复计算了三次先验概率、但其实只用计算一次就可以。考虑到这一点不是性能瓶颈,为了代码的连贯性和可读性、我们就没有进行这个优化(???)\u003C\u002Fp\u003E\u003Cp\u003E\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n数据转换函数则相对而言要复杂一点,因为我们需要跳过连续维度、将离散维度挑出来进行数值化:\u003Cbr\u003E\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003E
# 实现转换混合型数据的方法,要注意利用MultinomialNB的相应变量\n
def _transfer_x(self, x):\n
_feat_dics = self._multinomial[\&feat_dics\&]\n
for d, discrete in enumerate(self._whether_discrete):\n
# 如果是连续维度,直接调用float方法将其转为浮点数\n
if not discrete:\n
x[d] = float(x[d])\n
# 如果是离散维度,利用转换字典进行数值化\n
x[d] = _feat_dics[idx][x[d]]\n
if discrete:\n
idx += 1\n
return x\n\u003C\u002Fcode\u003E\u003Cp\u003E至此,混合型朴素贝叶斯模型就搭建完毕了。为了比较合理地对它进行评估,我们不妨采用 UCI 上一个我认为有些病态的数据集进行测试。问题的描述大概可以概括如下:\u003C\u002Fp\u003E\u003Cp\u003E\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n“训练数据包含了某银行一项业务的目标客户的信息、电话销售记录以及后来他是否购买了这项业务的信息。我们希望做到:根据客户的基本信息和历史联系记录,预测他是否会购买这项业务”。UCI 上的原问题描述则如下图所示:\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E\u003Cimg src=\&v2-a9b0d26d602d4c97c18caf72ecdb3b18.png\& data-rawwidth=\&674\& data-rawheight=\&223\&\u003E概括其主要内容、就是它是一个有 17 个属性的二类分类问题。之所以我认为它是病态的,是因为我发现即使是 17 个属性几乎完全一样的两个人,他们选择是否购买业务的结果也会截然相反。事实上从心理学的角度来说,想要很好地预测人的行为确实是一项非常困难的事情、尤其是当该行为直接牵扯到较大的利益时\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n完整的数据集可以参见\u003Ca href=\&https:\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Fblob\u002Fmaster\u002F_Data\u002Fbank1.0.txt\& data-editable=\&true\& data-title=\&这里\& class=\&\&\u003E这里\u003C\u002Fa\u003E(最后一列数据是类别)。按照数据的特性、我们可以通过和之前用来评估MultinomialNB的代码差不多的代码(注意额外定义一个记录离散型维度的数组即可)得出如下图所示的结果:\u003Cimg src=\&v2-441fd8d0dbad409a1261e8.png\& data-rawwidth=\&409\& data-rawheight=\&166\&\u003E\u003C\u002Fp\u003E\u003Cp\u003E虽然准确率达到了 89%左右,但其实该问题不应该用准确率作为评判的标准。因为如果我们观察数据就会发现、数据存在着严重的非均衡现象。事实上,88%的客户最终都是没有购买这个业务的、但我们更关心的是那一小部分购买了业务的客户,这种情况我们通常会用 F1-score 来衡量模型的好坏。此外,该问题非常需要人为进行数据清洗、因为其原始数据非常杂乱。此外,我们可以对该问题中的各个离散维度进行可视化。该数据共 9 个离散维度,我们可以将它们合并在同一个图中以方便获得该数据离散部分的直观(如下图所示;由于各个特征的各个取值通常比较长(比如\&manager\&之类的),为整洁、我们直接将横坐标置为等差数列而没有进行转换):\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cimg src=\&v2-da3f717a.png\& data-rawwidth=\&845\& data-rawheight=\&648\&\u003E\u003Cp\u003E其中天蓝色代表类别 yes、亦即购买了业务;橙色则代表 no、亦即没有购买业务。可以看到、所有离散维度的特征都是前面所说的“无足轻重”的特征\u003C\u002Fp\u003E\u003Cp\u003E\n\n连续维度的可视化是几乎同理的,唯一的差别在于它不是柱状图而是正态分布密度函数的函数曲线。具体的代码实现从略、感兴趣的观众老爷们可以尝试动手实现一下,这里仅放出程序运行的结果。该数据共 7 个连续维度,我们同样把它们放在同一个图中:\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cimg src=\&v2-8b72cfdc983c22fb8d98becd73e21bf7.png\& data-rawwidth=\&861\& data-rawheight=\&665\&\u003E\u003Cp\u003E其中,天蓝色曲线代表类别 yes、橙色曲线代表类别 no。可以看到,两种类别的数据在各个维度上的正态分布的均值、方差都几乎一致\u003C\u002Fp\u003E\u003Cp\u003E\n\n从以上的分析已经可以比较直观地感受到、该问题确实相当病态。特别地,考虑到朴素贝叶斯的算法、不难想象此时的混合型朴素贝叶斯模型基本就只是根据各类别的先验概率来进行分类决策\u003Cbr\u003E\u003C\u002Fp\u003E\u003Cp\u003E至此,朴素贝叶斯算法的理论、实现就差不多都说了一遍,希望观众老爷们能够喜欢~\u003C\u002Fp\u003E&,&updated&:new Date(&T10:39:19.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:2,&likeCount&:12,&state&:&published&,&isLiked&:false,&slug&:&&,&isTitleImageFullScreen&:false,&rating&:&none&,&sourceUrl&:&&,&publishedTime&:&T18:39:19+08:00&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&url&:&\u002Fp\u002F&,&titleImage&:&https:\u002F\u002Fpic1.zhimg.com\u002Fv2-1bd06a58b5ddec7b16fa6c03aac93009_r.jpg&,&summary&:&&,&href&:&\u002Fapi\u002Fposts\u002F&,&meta&:{&previous&:null,&next&:null},&snapshotUrl&:&&,&commentsCount&:2,&likesCount&:12},&&:{&title&:&Python · CNN(一)· 层结构&,&author&:&carefree0910&,&content&:&\u003Cp\u003E(这里是最终成品的 \u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Ftree\u002Fmaster\u002FNN\& class=\&\& data-editable=\&true\& data-title=\&GitHub\&\u003EGitHub\u003C\u002Fa\u003E 地址)\u003C\u002Fp\u003E\u003Cp\u003E(这里是本章用到的 \u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=https%3Ahttps:\u002F\u002Fgithub.com\u002FcarefreeFMachineLearning\u002Fblob\u002Fmaster\u002Fg_CNN\u002FLayers.py\& class=\&\& data-editable=\&true\& data-title=\&GitHub\&\u003EGitHub\u003C\u002Fa\u003E 地址) \u003C\u002Fp\u003E\u003Cp\u003E========== 写在前面的话 ==========\u003C\u002Fp\u003E\u003Cp\u003E其实在 4 个月之前我写过一篇叫“Python · 神经网络(八)· ConvLayer”的文章,不过现在看回去觉得写的有点太概括性了;如果直接往下写的话,估计观众老爷们(以及我自己)的逻辑都理不顺 _(:з」∠)_\u003C\u002Fp\u003E\u003Cp\u003E所以我打算重写一次,而且这次会对之前 NN 系列的文章做一个汇总性说明;换句话说,我会从头开始讲如何实现 CNN 而不是接着 NN 的逻辑来讲(这也是为什么我没有接着用“神经网络”这个系列名而是开了个新的“CNN”系列) _(:з」∠)_\u003C\u002Fp\u003E\u003Cp\u003E这意味着本文(及接下来的 CNN 系列)会巨长无比,毕竟我会试图把两三百行的东西一次性讲清楚 _(:з」∠)_\u003C\u002Fp\u003E\u003Cp\u003E如果觉得这些都无所谓并愿意看的话,我会觉得很开心的 _(:з」∠)_\u003C\u002Fp\u003E\u003Cp\u003E一些数学基础:\u003C\u002Fp\u003E\u003Cp\u003E\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-title=\&数学 · 神经网络(一)· 前向传导\& class=\&\& data-editable=\&true\&\u003E数学 · 神经网络(一)· 前向传导\u003C\u002Fa\u003E \u003C\u002Fp\u003E\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&数学 · 神经网络(二)· BP(反向传播)\& class=\&\&\u003E数学 · 神经网络(二)· BP(反向传播)\u003C\u002Fa\u003E\u003Cp\u003E\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-title=\&数学 · 神经网络(三)· 损失函数\& class=\&\& data-editable=\&true\&\u003E数学 · 神经网络(三)· 损失函数\u003C\u002Fa\u003E \u003C\u002Fp\u003E\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&数学 · CNN · 从 NN 到 CNN\&\u003E数学 · CNN · 从 NN 到 CNN\u003C\u002Fa\u003E\u003Cbr\u003E\u003Cp\u003E========== 分割线的说 ==========\u003C\u002Fp\u003E\u003Cp\u003E往简单里说、CNN 只是多了卷积层、池化层和 FC 的 NN 而已,虽然卷积、池化对应的前向传导算法和反向传播算法的高效实现都很不平凡,但得益于 Tensorflow 的强大、我们可以在仅仅知道它们思想的前提下进行相应的实现,因为 Tensorflow 能够帮我们处理所有数学与技术上的细节(Tensorflow 的应用式入门教程可以参见\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&这里\&\u003E这里\u003C\u002Fa\u003E)\u003C\u002Fp\u003E\u003Ch2\u003E实现普通层\u003C\u002Fh2\u003E\u003Cp\u003E我们在\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&Python · 神经网络(一)· 层\& class=\&\&\u003EPython · 神经网络(一)· 层\u003C\u002Fa\u003E和\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&Python · 神经网络(二)· 层\& class=\&\&\u003EPython · 神经网络(二)· 层\u003C\u002Fa\u003E里面非常琐碎地说明了如何实现 Layer 结构,这里我们就详尽地把整个实现捋一捋。鉴于 Tensorflow 能够自动获取梯度、同时考虑到要扩展出 CNN 的功能,我们需要实现如下功能:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E对于激活函数,只用定义其原始形式、不必定义其导函数形式\u003C\u002Fli\u003E\u003Cli\u003E解决特殊层结构(\u003Ca href=\&https:\u002F\u002Fwww.cs.toronto.edu\u002F~hinton\u002Fabsps\u002FJMLRdropout.pdf\& data-editable=\&true\& data-title=\&Dropout\& class=\&\&\u003EDropout\u003C\u002Fa\u003E、\u003Ca href=\&https:\u002F\u002Farxiv.org\u002Fpdf\u002F.pdf\& data-editable=\&true\& data-title=\&Normalize\&\u003ENormalize\u003C\u002Fa\u003E 等等)的实现问题 \u003C\u002Fli\u003E\u003Cli\u003E要考虑当前层为 FC(全连接层)时的表现\u003C\u002Fli\u003E\u003Cli\u003E让用户可以选择是否给 Layer 加偏置量\n\n\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E其中的第四点可能有些让人不明所以:要知道偏置量可是对破坏对称性是很重要的,为什么要让用户选择是否使用偏置量呢?这主要是因为特殊层结构中 Normalize 的特殊性会使偏置量显得冗余。具体细节会在后文讨论特殊层结构处进行说明,这里就暂时按下不表\u003C\u002Fp\u003E\u003Cp\u003E以下是 Layer 结构基类的具体代码:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eimport numpy as np\nimport tensorflow as tf\nfrom math import ceil\n\nclass Layer:\n
初始化结构\n
self.shape:记录该Layer和上个Layer所含神经元的个数,具体而言:\n
self.shape[0] = 上个Layer所含神经元的个数\n
self.shape[1] = 该Layer所含神经元的个数\n
self.is_fc、self.is_sub_layer:记录该Layer是否为FC、特殊层结构的属性\n
self.apply_bias:记录是否对该Layer加偏置量的属性\n
def __init__(self, shape, **kwargs):\n
self.shape = shape\n
self.is_fc = self.is_sub_layer = False\n
self.apply_bias = kwargs.get(\&apply_bias\&, True)\n\n
def __str__(self):\n
return self.__class__.__name__\n\n
def __repr__(self):\n
return str(self)\n\n
@property\n
def name(self):\n
return str(self)\n\n
@property\n
def root(self):\n
return self\n\n
# 定义兼容特殊层结构和CNN的、前向传导算法的封装\n
def activate(self, x, w, bias=None, predict=False):\n
# 如果当前层是FC、就需要先将输入“铺平”\n
if self.is_fc:\n
x = tf.reshape(x, [-1, int(np.prod(x.get_shape()[1:]))])\n
# 如果是特殊的层结构、就调用相应的方法获得结果\n
if self.is_sub_layer:\n
return self._activate(x, predict)\n
# 如果不加偏置量的话、就只进行矩阵相乘和激活函数的作用\n
if not self.apply_bias:\n
return self._activate(tf.matmul(x, w), predict)\n
# 否则就进行“最正常的”前向传导算法\n
return self._activate(tf.matmul(x, w) + bias, predict)\n\n
# 前向传导算法的核心、留待子类定义\n
def _activate(self, x, predict):\n
pass\n\u003C\u002Fcode\u003E\u003Cp\u003E注意到我们前向传导算法中有一项“predict”参数,这主要是因为特殊层结构的训练过程和预测过程表现通常都会不一样、所以要加一个标注。该标注的具体意义会在后文进行特殊层结构 SubLayer 的相关说明时体现出来、这里暂时按下不表\u003C\u002Fp\u003E\n\n\u003Cp\u003E在实现好基类后、就可以实现具体要用在神经网络中的 Layer 了。以 Sigmoid 激活函数对应的 Layer 为例:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eclass Sigmoid(Layer):\n
def _activate(self, x, predict):\n
return tf.nn.sigmoid(x)\n\u003C\u002Fcode\u003E\u003Cp\u003E得益于 Tensorflow 框架的强大(你除了这句话就没别的话说了吗……)、我们甚至连激活函数的形式都无需手写,因为它已经帮我们封装好了(事实上、绝大多数常用的激活函数在 Tensorflow 里面都有封装)\u003C\u002Fp\u003E\u003Ch2\u003E实现特殊层\u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E我们在\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F?refer=carefree0910-pyml\& data-editable=\&true\& data-title=\&Python · 神经网络(三*)· 网络\& class=\&\&\u003EPython · 神经网络(三*)· 网络\u003C\u002Fa\u003E这里曾经简要介绍过特殊层 SubLayer 的思想,这里我们将介绍如何利用 Tensorflow 框架实现它,同时也会对十分常用的两种 SubLayer —— Dropout 和 Normalize 做深入一些的介绍\u003C\u002Fp\u003E\n\n先来看看应该如何定义 SubLayer 的基类:\u003Ccode lang=\&python\&\u003E# 让SubLayer继承Layer以合理复用代码\nclass SubLayer(Layer):\n
初始化结构\n
self.shape:和Layer相应属性意义一致\n
self.parent:记录该Layer的父层的属性\n
self.description:用于可视化的属性,记录着对该SubLayer的“描述”\n
def __init__(self, parent, shape):\n
Layer.__init__(self, shape)\n
self.parent = parent\n
self.description = \&\&\n\n
# 辅助获取Root Layer的property\n
@property\n
def root(self):\n
_root = self.parent\n
while _root.parent:\n
_root = _root.parent\n
return _root\n\u003C\u002Fcode\u003E\u003Cp\u003E可以看到,得益于 Tensorflow 框架(Tensorflow 就是很厉害嘛……),本来难以处理的SubLayer 的实现变得非常简洁清晰。在实现好基类后、就可以实现具体要用在神经网络中的 SubLayer 了,先来看 Dropout:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eclass Dropout(SubLayer):\n
# self._prob:训练过程中每个神经元被“留下”的概率\n
def __init__(self, parent, shape, drop_prob=0.5):\n
# 神经元被Drop的概率必须大于等于0和小于1\n
if drop_prob & 0 or drop_prob &= 1:\n
raise ValueError(\n
\&(Dropout) Probability of Dropout should be a positive float smaller than 1\&)\n
SubLayer.__init__(self, parent, shape)\n
# 被“留下”的概率自然是1-被Drop的概率\n
self._prob = tf.constant(1 - drop_prob, dtype=tf.float32)\n
self.description = \&(Drop prob: {})\&.format(drop_prob)\n\n
def _activate(self, x, predict):\n
# 如果是在训练过程,那么就按照设定的、被“留下”的概率进行Dropout\n
if not predict:\n
return tf.nn.dropout(x, self._prob)\n
# 如果是在预测过程,那么直接返回输入值即可\n
return x\n\u003C\u002Fcode\u003E\u003Cp\u003EDropout 的详细说明自然是看\u003Ca href=\&https:\u002F\u002Fwww.cs.toronto.edu\u002F~hinton\u002Fabsps\u002FJMLRdropout.pdf\& data-editable=\&true\& data-title=\&原 paper \&\u003E原 paper \u003C\u002Fa\u003E最好,这里我就大概翻译、总结一下主要内容。Dropout 的核心思想在于提高模型的泛化能力:它会在每次迭代中依概率去掉对应 Layer 的某些神经元,从而每次迭代中训练的都是一个小的神经网络。这个过程可以通过下图进行说明:\u003C\u002Fp\u003E\u003Cimg src=\&v2-f51e72018ae4.png\& data-rawwidth=\&551\& data-rawheight=\&233\&\u003E\u003Cp\u003E上图所示的即为当 drop_prob 为 50%(我们所设的默认值)时、Dropout 的一种可能的表现。左图所示为原网络、右图所示的为 Dropout 后的网络,可以看到神经元 a、b、e、g、j 都被 Drop 了\u003C\u002Fp\u003E\u003Cp\u003EDropout 过程的合理性需要概率论上一些理论的支撑,不过鉴于 Tensorflow 框架有封装好的相应函数、我们就不深入介绍其具体的数学原理而仅仅说明其直观(以 drop_prob 为 50%为例,其余 drop_prob 的情况是同理的):\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E在训练过程中,由于 Dropout 后留下来的神经元可以理解为“在 50%死亡概率下幸存”的神经元,所以给将它们对应的输出进行“增幅”是合理的。具体而言,假设一个神经元\u003Cequation\u003En_i\u003C\u002Fequation\u003E的输出本来是\u003Cequation\u003Eo_i\u003C\u002Fequation\u003E,那么如果 Dropout 后它被留下来了的话、其输出就应该变成\u003Cequation\u003Eo_i\\times\\frac1{50\\%}=2o_i\u003C\u002Fequation\u003E(换句话说、应该让带 Dropout 的期望输出和原输出一致:对于任一个神经元\u003Cequation\u003En_i\u003C\u002Fequation\u003E,设 drop_prob 为\u003Ci\u003E p\u003C\u002Fi\u003E 而其原输出为\u003Cequation\u003Eo_i\u003C\u002Fequation\u003E,那么当带 Dropout 的输出为\u003Cequation\u003Eo_i\\times\\frac1p\u003C\u002Fequation\u003E时、\u003Cequation\u003En_i\u003C\u002Fequation\u003E的期望输出即为\u003Cequation\u003Ep\\times o_i\\times\\frac1p=o_i\u003C\u002Fequation\u003E) \u003C\u002Fli\u003E\u003Cli\u003E由于在训练时我们保证了神经网络的期望输出不变、所以在预测过程中我们还是应该让整个网络一起进行预测而不进行 Dropout(关于这一点,原论文似乎也表示这是一种“经试验证明行之有效”的办法而没有给出具体的、原理层面的说明)\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003ENormalize 说起来有点长,所以我开了一个单独的章节来说(\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F?refer=carefree0910-pyml\& data-title=\&数学 · 神经网络(四)· Normalize\& class=\&\& data-editable=\&true\&\u003E数学 · 神经网络(四)· Normalize\u003C\u002Fa\u003E)。下面就直接看看如何实现它:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003Eclass Normalize(SubLayer):\n
初始化结构\n
self._eps:记录增强数值稳定性所用的小值的属性\n
self._activation:记录自身的激活函数的属性,主要是为了兼容图7.17 A的情况\n
self.tf_rm、self.tf_rv:记录μ_run、σ_run^2的属性\n
self.tf_gamma、self.tf_beta:记录γ、β的属性\n
self._momentum:记录动量值m的属性\n
def __init__(self, parent, shape, activation=\&Identical\&, eps=1e-8, momentum=0.9):\n
SubLayer.__init__(self, parent, shape)\n
self._eps, self._activation = eps, activation\n
self.tf_rm = self.tf_rv = None\n
self.tf_gamma = tf.Variable(tf.ones(self.shape[1]), name=\&norm_scale\&)\n
self.tf_beta = tf.Variable(tf.zeros(self.shape[1]), name=\&norm_beta\&)\n
self._momentum = momentum\n
self.description = \&(eps: {}, momentum: {})\&.format(eps, momentum)\n\n
def _activate(self, x, predict):\n
# 若μ_run、σ_run^2还未初始化,则根据输入x进行相应的初始化\n
if self.tf_rm is None or self.tf_rv is None:\n
shape = x.get_shape()[-1]\n
self.tf_rm = tf.Variable(tf.zeros(shape), trainable=False, name=\&norm_mean\&)\n
self.tf_rv = tf.Variable(tf.ones(shape), trainable=False, name=\&norm_var\&)\n
if not predict:\n
# 利用Tensorflow相应函数计算当前Batch的举止、方差\n
_sm, _sv = tf.nn.moments(x, list(range(len(x.get_shape()) - 1)))\n
_rm = tf.assign(\n
self.tf_rm, self._momentum * self.tf_rm + (1 - self._momentum) * _sm)\n
_rv = tf.assign(\n
self.tf_rv, self._momentum * self.tf_rv + (1 - self._momentum) * _sv)\n
# 利用Tensorflow相应函数直接得到Batch Normalization的结果\n
with tf.control_dependencies([_rm, _rv]):\n
_norm = tf.nn.batch_normalization(\n
x, _sm, _sv, self.tf_beta, self.tf_gamma, self._eps)\n
_norm = tf.nn.batch_normalization(\n
x, self.tf_rm, self.tf_rv, self.tf_beta, self.tf_gamma, self._eps)\n
# 如果指定了激活函数、就再用相应激活函数作用在BN结果上以得到最终结果\n
# 这里只定义了ReLU和Sigmoid两种,如有需要可以很方便地进行拓展\n
if self._activation == \&ReLU\&:\n
return tf.nn.relu(_norm)\n
if self._activation == \&Sigmoid\&:\n
return tf.nn.sigmoid(_norm)\n
return _norm\n\u003C\u002Fcode\u003E\u003Ch2\u003E实现损失层\u003Cbr\u003E\u003C\u002Fh2\u003E\u003Cp\u003E我们在\u003Ca href=\&https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F\& data-editable=\&true\& data-title=\&Python · 神经网络(五)· Cost & Optimizer\&\u003EPython · 神经网络(五)· Cost & Optimizer\u003C\u002Fa\u003E里曾经说明过如何实现损失层,这里就简单地重复一下:\u003C\u002Fp\u003E\u003Ccode lang=\&python\&\u003E# 定义一个简单的基类\nclass CostLayer(Layer):\n
# 定义一个方法以获取损失值\n
def calculate(self, y, y_pred):\n
return self._activate(y_pred, y)\n\n# 定义Cross Entropy对应的CostLayer(整合了Softmax变换)\nclass CrossEntropy(CostLayer):\n
def _activate(self, x, y):\n
return tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=x, labels=y))\n\n# 定义MSE准则对应的CostLayer\nclass MSE(CostLayer):\n
def _activate(self, x, y):\n
return tf.reduce_mean(tf.square(x - y))\n\u003C\u002Fcode\u003E\u003Cp\u003E我自己用 Numpy 写的话,相同功能要写那么个 113 行,然后用 Tensorflow 的话 15 行就行了……由此可窥见 Tensorflow 框架的强大\u003C\u002Fp\u003E\u003Cp\u003E(话说我这么卖力地安利 Tensorflow,Google

我要回帖

更多关于 python中featvec 的文章

 

随机推荐