网络程序设计结课总结——神经网络篇

来源:互联网 发布:新碧水感防晒露 知乎 编辑:程序博客网 时间:2024/05/16 07:45

前情提要:

为什么深度学习逐渐成为热潮?
随着计算机运算速度的提高和硬件条件的不断改善,神经网络和深度学习逐渐涉及到了生活中的方方面面,成为了当今的一个热门的话题。
我作为初入编程的菜鸟,代码能力比较差,因为本科专业的原因,所以有一定的数学基础,也因此最终选择了孟宁老师的网络程序设计课程。
这学期的网络程序设计课程主要内容是,基于深度学习神经网络等机器学习技术实现一个医学辅助诊断的专家系统原型,具体切入点为:为血常规检验报告的OCR识别,深度学习与分析,神经网络基本内容的介绍(PPT展示),以及一些常见神经网络工具和API库的使用和比较分析。博客的主要内容分成三部分来进行展示。分别是课程项目的整体 工作流程和代码功能展示,我在其中做的功劳和苦劳,以及课程结束后的收获与感悟。因为经验不足和第一次写博客,有不足和不明的地方,还希望大家和我一起讨论可怜~
(为了更好的展示整体流程和设计思路,博客里的部分图片来自网络和同学!!!侵权删么么哒!!!)
博客里涉及的主要工具有Tensorflow/spss/octave,版本都更新至最新版。Python版本为2.7。3以上可能不能运行文章中涉及的部分代码。

课程项目整体展示

课程项目A1:神经网络实现手写字符识别系统

因为是刚入门的项目,所以难度不是很大(代码菜鸟啪啪打脸),主要是在实验楼进行了代码实现,整个流程实验楼里总结的非常完整,对于新手来说非常容易上手,很利于培养自信心(……),所以作为一只小白我就带着这种迷之自信入了坑。
但是其实说实话,我一开始对图片的转换以及其中的各种神经网络完全是大写的懵逼,虽然在大神的帮助下成功的跑出了结果但是可以说完全没有任何理解。
作为一只菜鸟,这时候我甚至还不是很理解机器学习、神经网络、深度学习之间的区别…
这里要强势安利一波coursera的神经网络课程!(答应我链接请自己谷歌好嘛)
在经历几周的学习以后,我发现手写字符识别真的是神经网络里非常基础而且用途非常广泛的练手实例。TensorFlow的入门教程、octave跑的第一个实例,以及各种教学课程上都会使用它来带新手入门,所以在挣扎摸索了很久后,我终于用Octave自己跑出了手写字符识别系统的代码。
参与测试的字符串
↑这里是参与测试的字符串的图片。他们都已经被预处理为20*20的格式,一共有5000个数据(随机选择了其中的100个进行输出显示),被传入存储为一个5000*400的矩阵,其中每一行代表了一个独立的字符。
手写字符识别系统本质上是一个多分类问题,为了更好的理解神经网络的优越性,这里我选择使用线性分类和神经网络两种模式进行了手写字符识别系统,在模型训练较好的前提下,二者的精度分别在94和97左右,
对整个迭代过程进行了输出展示
线性拟合:按照分类器的数量,用for循环进行了分别迭代,迭代次数为50次,并计算代价函数,最后为预测精度。

这里是迭代部分的代码:

%    Set Initial thetafor c = 1:num_labels    initial_theta = zeros(n + 1, 1);%     %    Set options for fminunc     options = optimset('GradObj', 'on', 'MaxIter', 50);% %    Run fmincg to obtain the optimal theta%    This function will return theta and the cost      [theta] =fmincg (@(t)(lrCostFunction(t, X, (y == c), lambda)),initial_theta, options);      all_theta(c,:)=theta';     fprinf('zheshi',theta);

神经网络:

这里神经网络的参数是课程里直接给出的,打包成了weight数据文件,在执行的时候对预测环节加入了display部分,对每一个随机抽出的图像进行了还原展示,将标签和真实值对比。
预测部分的代码如下:

function p = predict(Theta1, Theta2, X)%PREDICT Predict the label of an input given a trained neural network%   p = PREDICT(Theta1, Theta2, X) outputs the predicted label of X given the%   trained weights of a neural network (Theta1, Theta2)% Useful valuesm = size(X, 1);num_labels = size(Theta2, 1);% You need to return the following variables correctly p = zeros(size(X, 1), 1);% ====================== YOUR CODE HERE ======================% Instructions: Complete the following code to make predictions using%               your learned neural network. You should set p to a %               vector containing labels between 1 to num_labels.%% Hint: The max function might come in useful. In particular, the max%       function can also return the index of the max element, for more%       information see 'help max'. If your examples are in rows, then, you%       can use max(A, [], 2) to obtain the max for each row.% X=[ones(m,1) X]; a=sigmoid(X*Theta1'); a=[ones(m,1) a ]; b=sigmoid(a*Theta2'); [B,BX]=max(b,[],2); p=BX;

经过这番的比对分析,令我对手写字符识别的整个系统有了更深入的理解。

课程项目A2 血常规检验报告图像的OCR识别

这里我们期待的设计效果是,能够从一个完整的体检报告上手工截图文字和数字的图片作为输入进行OCR识别。对一个非标准的体检报告图能够正确识别表格的区域, 如果图片不能识别或者数据出现错误能够return一个错误信息,以使用户能够重新上传图片。
这里主要的设计思路是,通过对表格边缘三道黑线的识别来进行定位,从而正确分辨表格区域。在实际操作中还利用投射原理,对非正常位置的体检报告图进行正置标准化调整。

这里博客内容参考了班里同学的整理内容,因为感觉她的整理更简单明了和全面~
附上原作者的博客直达链接→点这里点这里

 view.py --Web 端上传图片到服务器,存入mongodb并获取oid。 imageFilter.py --对图像透视裁剪和OCR进行了简单的封装,便于模块间的交互,规定适当的接口.是整个ocr中最重要的模块. classifier.py --用于判定裁剪矫正后的报告和裁剪出检测项目的编号 imgproc.py --将识别的图像进行处理二值化等操作,提高识别率

ocr主要使用了opencv2包。

1 对输入的图像进行处理,采用Canny算子描绘边缘

img_sp = self.img.shaperef_lenth = img_sp[0] * img_sp[1] * ref_lenth_multiplierimg_gray = cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)img_gb = cv2.GaussianBlur(img_gray, (gb_param, gb_param), 0)closed = cv2.morphologyEx(img_gb, cv2.MORPH_CLOSE, kernel)opened = cv2.morphologyEx(closed, cv2.MORPH_OPEN, kernel)edges = cv2.Canny(opened, canny_param_lower , canny_param_upper)

2 调用CV2模块中findContours函数来提取矩形轮廓,设定阈值,并筛选对角线大于阈值的轮廓

这里有点问题是,因为老师一开始只给了一张实物图,所以在参数设置的时候,很多设置是有针对性的(比如数据种类以及三道横线…),在普适性上还需要继续改进。

contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)def getbox(i):rect = cv2.minAreaRect(contours[i])box = cv2.cv.BoxPoints(rect)box = np.int0(box)return boxdef distance(box):delta1 = box[0]-box[2]delta2 = box[1]-box[3]distance1 = np.dot(delta1,delta1)distance2 = np.dot(delta2,delta2)distance_avg = (distance1 + distance2) / 2return distance_avg# 筛选出对角线足够大的几个轮廓found = []for i in range(len(contours)):box = getbox(i)distance_arr = distance(box)if distance_arr > ref_lenth:found.append([i, box])def getline(box):if np.dot(box[1]-box[2],box[1]-box[2]) < np.dot(box[0]-box[1],box[0]-box[1]):point1 = (box[1] + box[2]) / 2point2 = (box[3] + box[0]) / 2lenth = np.dot(point1-point2, point1-point2)return point1, point2, lenthelse:point1 = (box[0] + box[1]) / 2point2 = (box[2] + box[3]) / 2lenth = np.dot(point1-point2, point1-point2)return point1, point2, lenthdef cmp(p1, p2):delta = p1 - p2distance = np.dot(delta, delta)if distance < img_sp[0] * img_sp[1] * ref_close_multiplier:return 1else:return 0def linecmp(l1, l2):f_point1 = l1[0]f_point2 = l1[1]f_lenth = l1[2]b_point1 = l2[0]b_point2 = l2[1]b_lenth = l2[2]if cmp(f_point1,b_point1) or cmp(f_point1,b_point2) or cmp(f_point2,b_point1) or cmp(f_point2,b_point2):if f_lenth > b_lenth:return 1else:return -1else:return 0def deleteline(line, j):lenth = len(line)for i in range(lenth):if line[i] is j:del line[i]return

3 把轮廓变成线,并去除不合适的线

# 比较最小外接矩形相邻两条边的长短,以两条短边的中点作为线的两端line = []for i in found:box = i[1]point1, point2, lenth = getline(box)line.append([point1, point2, lenth])# 把不合适的线删去if len(line)>3:for i in line:for j in line:if i is not j:rst = linecmp(i, j)if rst > 0:deleteline(line, j)elif rst < 0:deleteline(line, i)#检测出的线数量不对就返回-1跳出if len(line) != 3:print "it is not a is Report!,len(line) =",len(line)return Nonedef distance_line(i, j):dis1 = np.dot(i[0]-j[0], i[0]-j[0])dis2 = np.dot(i[0]-j[1], i[0]-j[1])dis3 = np.dot(i[1]-j[0], i[1]-j[0])dis4 = np.dot(i[1]-j[1], i[1]-j[1])return min(dis1, dis2, dis3, dis4)def findhead(i, j, k):dis = []dis.append([distance_line(i, j), i, j])dis.append([distance_line(j, k), j, k])dis.append([distance_line(k, i), k, i])dis.sort()if dis[0][1] is dis[2][2]:return dis[0][1], dis[2][1]if dis[0][2] is dis[2][1]:return dis[0][2], dis[2][2]def cross(vector1, vector2):return vector1[0]*vector2[1]-vector1[1]*vector2[0]

这里主要是各位大牛的突出贡献~运用了透视变换和一系列API,并根据实际使用时的情况进行了不断的细节调整。菜鸟表示只能简单理解代码,为了方便回顾这里附上了代码,但是就不多做介绍了,期待大牛们的分享。

课程项目A3 根据血常规检验的各项数据预测年龄和性别

在能够对血常规检验报告单进行数据化处理后,我们将利用血常规检验分析单中的各种数据进行数据分析,以期待发现这二十多项中的内在联系,并希望构建一种模型,能够较为稳定和准确的预测不同患者的年龄和性别。
因为数据来源的不断更新,我们主要进行了两次模型构建,训练样本分别为2000个和8500个左右,并进行了一系列数据相关性的基础分析。
这里我们主要使用的工具为TensorFlow,我还额外使用了spss对数据进行了一些基本处理和相关性分析,根据得出来的结论,调整模型中各项数据的比重和神经网络的权重,现在通过血常规的检验数据我们可以较为准确的判断出患者的性别,准确率可以达到百分之八十以上。但是年龄即使经过分箱后,也只有百分之三十左右的预测准确率。
最后,按照老师的要求,我们将A3的模型整合到A2的OCR中,完成了一个完整的web系统。这里有很多大牛进行了前段和后端的贡献,作为一只菜鸡只能默默的膜拜并进行了阅读…大家跑出来的结果大同小异,就不进行细致的解说了,依旧期待大牛分享代码经验。
↓以下是部分代码和运行时候的截图。
初始页面
选择图片上传
生成OCR数据单
进行性别和年龄预测
(这里参考了田奇同学的博客内容,具体细节分析请戳博客正文)

我在其中做出的功劳和苦劳

(以下是王婆卖瓜环节,贡献微薄请各位大神轻拍~)

机器学习基础知识相关的PPT制作与展示

在这个部分里,我主要讲解了机器学习中的随机森林部分知识介绍,具体的PPT已经上传到了课程后面的附件里。
随机森林PPT截图
随机森林是一种比较新的机器学习模型。经典的机器学习模型是神经网络,有半个多世纪的历史了。神经网络预测精确,但是计算量很大。上世纪八十年代Breiman等人发明分类树的算法(Breiman et al. 1984),通过反复二分数据进行分类或回归,计算量大大降低。2001年Breiman把分类树组合成随机森林(Breiman 2001a),即在变量(列)的使用和数据(行)的使用上进行随机化,生成很多分类树,再汇总分类树的结果。随机森林在运算量没有显著提高的前提下提高了预测精度。随机森林对多元共线性不敏感,结果对缺失数据和非平衡的数据比较稳健,可以很好地预测多达几千个解释变量的作用(Breiman 2001b),被誉为当前最好的算法之一(Iverson et al. 2008)。我主要讲解了什么是随机森林——随机森林的基本概念,随机森林的一些经典算法,和随机森林擅长解决的一些问题和构建随机森林模型时候会遇到的一些细节问题的解决。

在后面的实际操作环节,因为数据量的不足,所以神经网络并没有特别突出的显示出了它的优越性,而因为数据缺失和各种现实条件限制,也有很多同学选择了在分类问题中多次使用随机森林的模型,并取得了较好的预测效果,这也证明了随机森林在具体用例上的实用性和普适性。

血常规检验的各项数据预测年龄和性别中的数据处理部分

从现实生活中转换过来的数据,常常存在数据缺失,噪声大,特征值多,相关性高等缺点。在进行神经网络或者是利用线性拟合训练模型的时候,在参数一定的情况下,对数据进行一定程度的初处理是一种非常通用,且便捷提高模型准确度的方法。
在实际项目中,预测的准确度很长时间一直卡在一个较低的水平无法提升,且前后两次数据源的不同导致了特征值有所变化,所以进行数据的初处理是十分重要的。
这里强烈安利一发SPSS
因为对代码不是很熟悉,所以我这里选择了一个简单快捷的工具来实现数据的初处理。其中包括了:
(如果对公式比较烦躁的请直接看黑体字标题就好~)

标准化(对缺失数据进行删除和补全)

这里写图片描述

正规化

这里写图片描述

归一化(将数据映射到一个相同或者近似的区间里,从而使得模型的参数范围更相似,模型更稳健)

这里写图片描述

降维和相关性分析(进行特征值的选择和模型的简化)

这里提供我根据老师给出的数据做出的一些和年龄和性别预测有关的相关性分析截图:
这里写图片描述
模型摘要
訓練 平方和錯誤 241.119
不正確預測數百分比 23.0%
已使用中止規則 1 連續步驟(含錯誤縮減)a
訓練時間 0:00:00.51
Testing 平方和錯誤 123.145
不正確預測數百分比 26.9%
應變數:sex
a. 錯誤計算是以測試樣本為基礎。
↑ 预测精度,年龄的因为预测度不高就不贴出来了。

课程结束后的收获与感悟

这里我想分为两部分进行叙述,一部分是根据自己的使用经历对博客中提及的各种工具的总结和推荐,另一部分是课程中思想上的一些变化和实质上的收获。

代码小白对一系列工具的评价~

作为一只代码小白,我大概唯一的优势是因为偷懒和懵逼尝试了比大牛们更多的工具…所以也多多少少另辟蹊径的做出了一点贡献。正如angrew在机器学习 这门课程中所提到的,对不同的问题,选择不同的工具来进行处理,会让你的整个实践过程事半功倍。随着神经网络的快速发展和IT行业涉及生活方方面面的深入,越来越多利于开发者的软件在逐步被开发出来,他们各有侧重点,适用于不同的环境和用户。这里想要简单的谈一谈对我在整个项目中使用工具的感受和推荐。

正如前情提要中说过的,我主要使用了TensorFlow、spss、octave三种工具。其中TensorFlow是我第一次尝试使用,第一感受是十分不好上手,对待新手和代码小白十分不友好,刚开始使用的时候真的是大写的懵逼。但是它在神经网络的学习和研究中使用非常广泛,也是我们这门课程中使用人数最多的工具之一,它具有比较良好的受众基础以及各种较为简单被调用的API。现在网络上它的课程和教程也非常多,所以以后我还是会选择深入学习下去,进一步加深对这个工具的理解和使用。

SPSS和SAS都比较多的被用在数据分析领域,其中SAS更专业化,但是同理也更大(大概25G左右…),大家可以根据自己的需求酌情选择。SPSS对用户比较友好,使用起来非常简单,进行主成分分析,数据标准化,相关性分析,模型可视化等非常的方便快捷。我个人的使用经验是,在进行代码训练之前,跑一下相关性分析来对模型的预测效果有一个比较全局的概念,以及进行简单的降维处理是十分有用的。同时,spss也可以进行简单的线性拟合、神经网络构建。可以说在数据量不大的前提下,我们完全可以不用一行代码,跑出一个高效的神经网络~缺点大概就是,黑箱操作过多,对程序员们来说,可调整的范围比较少,对某些单一的功能或者较大的数据可能不友好。

octave是MATLAB的简化版,所以有着和它极为相似的特点:代码简单,内部函数功能齐全,对功能的原理要求比代码本身更高。它和MATLAB比起来最大的优势就是小。。。大概只有几百m,MATLAB一般可以很轻松的上5G。这个工具我是看了Coursera上的安利才使用的,只能说用户体验非常的好!比起繁琐的代码debug,它支持更多的函数,且函数功能更为完善,也因此,它更关注对算法本身的理解而不是代码中的一些细节错误(虽然这些也很重要,但是对于刚开始学习的我们来说他们并不是最主要的)。我们可以选择在octave中实现我们的算法思路和不同方法之间的对比模拟,然后选择最优的方法来移植到TensorFlow和Python中,这被证明是十分简洁有效的。

课程相关的一些碎碎念~

作为一个初学者。也许一开始的时候懵逼而懵懂,但是在这门课将要结束的今天回头看,我会发现孟宁老师对课程的设计和安排是十分合理的。从一开始神经网络基础知识的PPT展示,让我们对神经网络的一些基本概念和算法有了认识,到后面转化为真正的、可以运行的一个完整的实践项目。从一开始什么都不懂完全照搬实验楼的代码,到后面会利用自己的优势对项目提供一些微薄的帮助,对不同领域的知识有了更深的知识。这里面蕴含着将近70个同学一个学期共同的努力,同学和老师互相讨论, 共同进步。其中大家的热心、好学和勤奋给我留下了深刻的印象。虽然我还是一只菜鸡,但是我至少目睹了大牛们的风采~
在整个课程期间,我跟随着着项目的发展,也在不断的扩展着自己的知识宽度,孟宁老师的一句话很有道理:“项目是强大的驱动力”。
神经网络这个方向,是一个日新月异、知识不断更替的领域,也许现在的我只是简单的窥见了一丝它的神奇与神秘,站在大神的肩膀上看到了一些东西真正的转化为了有用的、可以看见的实践成果。但是我相信,在老师的引导下、在同学的帮助下,我会走的更长更远。

0 0