网络程序设计总结 柳晓鹏

前言

今年有幸选到了孟宁老师的网络程序设计,这门课教授的是当今火热的深度学习技术,通过这门课的学习,我有了很多的心得和体会。

课程介绍

基于深度学习神经网络等机器学习技术实现一个医学辅助诊断的专家系统原型,具体切入点为课程项目:对血常规检验报告的OCR识别、深度学习与分析。

项目地址

  • 实验项目
  • 课程项目
  • 个人fork地址

个人贡献

这里写图片描述

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

该项目是利用Python和Python的numpy库写一个神经网络手写字符识别系统。通过使用5000x400的带标签的手写字符数据训练一个神经网络模型,并使用BP反向传播算法对参数进行校正,最终建立一个手写字符训练模型。人们可以在web端的canvas画布上手写数字,当点击Test的时候,就会将该手写字符数据传输到训练模型中,然后返回其预测值;当点击Train的时候,就可以根据人提供的真实数据进行训练。本次项目的教程地址在这里。关于什么是BP反向传播算法,这个博客给予了详细的说明。本次项目源代码在这里。

环境搭建

# 安装pip
sudo apt-get install python-pip

# 用pip安装numpy和scipy
pip install numpy scipy

# 如果上一步安装失败就使用ubuntu的包管理器试试
sudo apt-get install python-numpy python-scipy

# 安装sklearn, neural_network_design.py需要调用它做交叉验证
pip install -U scikit-learn -i https://pypi.mirrors.ustc.edu.cn/simple

# 下载图像和标签数据
wget http://labfile.oss.aliyuncs.com/courses/593/data.csv 
wget http://labfile.oss.aliyuncs.com/courses/593/dataLabels.csv

运行

# 训练模型
python neural_network_design.py
# 创建服务器
python -m SimpleHTTPServer 3000
# 加载服务器
python server.py
# 访问
localhost:3000

截图

网络程序设计总结 柳晓鹏_第1张图片

实验心得总结

通过本次实验的学习,我了解了前馈神经网络算法和BP反向传播算法的工作原理,另外也熟悉了神经网络的构建方式和python numpy库的使用,算是对神经网络有了一个初步的了解和认识,神经网络也没有以前觉得那么高不可及了。

课程项目A1a:使用深度学习框架改进A1

目前有很多深度学习框架,如Tensorflow,Caffe, Mxnet, Keras等等,由于目前谷歌的深度学习框架Tensorflow性能很好,而且学习资源很多,因此我选择了Tensorflow作为本次项目的深度学习框架。本次项目是对A1的改进,这里我主要参考了tensorflow官方mnist手写字符识别教程,使用60000个28x28的mnist手写字符数据集对tensorflow神经网络模型进行训练,并使用10000个28x28的mnist手写字符数据集作为预测识别成功率,该项目能够对用户输入的28x28的数字图片进行预测。本次项目源代码地址在这里。

环境配置

系统要求: ubuntu 14.04 / ubuntu 16.04

# 安装numpy,如果安装了,则跳过此步
sudo apt-get install python-numpy

# 安装PIL,如果安装过了,则跳过此步
sudo apt-get install python-imaging

# 安装Tensorflow
pip install --upgrade https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.12.0rc0-cp27-none-linux_x86_64.whl

# 下载mnist数据集,并把数据集放到TensorFlow下的mnist_data文件夹内  
https://pan.baidu.com/s/1bBI0Ku

代码

ImageReader.py

# -*- coding: UTF-8 -*-
import struct
import numpy as np
class ImageReader:
    def decode_idx3_ubyte(self, idx3_ubyte_file):
    # 读取二进制数据
        f = open(idx3_ubyte_file, 'rb')
        bin_data = f.read();
        f.close();
    # 解析文件头信息,依次为魔数、图片数量、每张图片高、每张图片宽
        offset = 0
        fmt_header = '>IIII'
        magic_number, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, bin_data, offset)
     #   print '魔数:%d, 图片数量: %d张, 图片大小: %d*%d' % (magic_number, num_images, num_rows, num_cols)
    # 解析数据集
        image_size = num_rows * num_cols
        offset += struct.calcsize(fmt_header)
        fmt_image = '>' + str(image_size) + 'B'
        images = np.empty((num_images, num_rows * num_cols))
        for i in range(num_images):
            images[i] = np.array(struct.unpack_from(fmt_image, bin_data, offset)).reshape((image_size))
            offset += struct.calcsize(fmt_image)
        return images

    def decode_idx1_ubyte(self, idx1_ubyte_file):
        bin_data = open(idx1_ubyte_file, 'rb').read()
    # 解析文件头信息,依次为魔数和标签数
        offset = 0
        fmt_header = '>ii'
        magic_number, num_images = struct.unpack_from(fmt_header, bin_data, offset)
      # print '魔数:%d, 图片数量: %d张' % (magic_number, num_images)
     # 解析数据集
        offset += struct.calcsize(fmt_header)
        fmt_image = '>B'
        labels = np.empty(num_images)
        for i in range(num_images):
            labels[i] = struct.unpack_from(fmt_image, bin_data, offset)[0]
            offset += struct.calcsize(fmt_image)
        return labels

    def load_images(self, imageSource):
        return self.decode_idx3_ubyte(imageSource);

    def load_labels(self, imageSource):
        return self.decode_idx1_ubyte(imageSource)

mnist.py

# -*- coding: UTF-8 -*-
from ImageReader import ImageReader;
import numpy as np
import tensorflow as tf;
from PIL import Image;

# 批处理,数据设为100
batch_size = 100;
image_size = 28 * 28;

class NeuralNetwork(object):
    def __init__(self):
        self.input = tf.placeholder("float", [None, image_size]);
        self.weight = tf.Variable(tf.zeros([image_size,10]), "weight")
        self.bias = tf.Variable(tf.zeros([10]), "bias")
        self.output = tf.nn.softmax(tf.matmul(self.input, self.weight) + self.bias)
        self.sess = tf.Session();

    def load_train_images(self):
        reader = ImageReader();
        train_images = reader.load_images("mnist_data/train-images.idx3-ubyte");
        train_labels = reader.load_labels("mnist_data/train-labels.idx1-ubyte");
        train_images /= 255.0;
        return train_images, train_labels;

    def load_test_images(self):
        reader = ImageReader();
        test_images = reader.load_images("mnist_data/t10k-images.idx3-ubyte");
        test_labels = reader.load_labels("mnist_data/t10k-labels.idx1-ubyte");
        test_images /= 255.0;
        return test_images, test_labels;

    def train(self):
        train_images, train_labels = self.load_train_images();
        dataset_size,length = train_images.shape;
        #target 是真实值
        target = tf.placeholder("float", [None,10])

        #cross_entorpy is a good cost function for softmax
        cross_entropy = -tf.reduce_sum(target * tf.log(self.output)) 
        train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

        labels = np.zeros((dataset_size, 10));
        for i in range(dataset_size):
            labels[i][int(train_labels[i])] = 1;

        self.sess.run(tf.global_variables_initializer())
        for i in range(0 , dataset_size, batch_size):
            batch_xs, batch_ys = train_images[i : i + batch_size] ,  labels[i : i + batch_size] ;
            self.sess.run(train_step, feed_dict={self.input: batch_xs, target : batch_ys})

    def predict_by_matrix(self, image_matrix):
        prediction = tf.argmax(self.output , 1);
        predict_value = prediction.eval(feed_dict = {self.input: image_matrix }, session = self.sess );
        return predict_value[0];

    def predict(self, imageSource):
        reader = ImageReader();
        im = Image.open(imageSource);
        assert im.size == (28, 28);
        # convert image to uint8 gray image
        im = im.convert("L") 
        data = np.matrix(im.getdata(),dtype='float')/255.0;

    def get_accuracy_rate(self):
        test_images, test_labels = self.load_test_images();
        test_images_num = test_images.shape[0]; 
        accuracy = 0;
        prediction = tf.argmax(self.output , 1);
        for i in range(test_images_num):
            #此处没有用self.predict(test_images[i])的原因是函数的调用大大降低了运行速度。
            #predict_value = self.predict_by_matrix(np.mat(test_images[i]));
            predict_value = prediction.eval(feed_dict = {self.input: np.mat(test_images[i])}, session = self.sess);

            #predict_value是一个只含有一个数据的list,比如[1],[2],[0]
            if (predict_value[0] == int(test_labels[i])):
                accuracy = accuracy + 1;
            #print "predict: ", predict_value[0], " real: " , test_labels[i];
        return  float(accuracy) / test_images_num;

if __name__ == '__main__':
    nn = NeuralNetwork();
    nn.train();
    # 28x28手写字符图片
    print nn.predict("test.bmp");
    accuracy_rate = nn.get_accuracy_rate();
    print "accuracy rate: " ,  accuracy_rate * 100 , "%";

网络程序设计总结 柳晓鹏_第2张图片

运行

python mnist.py

解释

  • ImageReader.py: ImageReader.py封装了一个可以读取mnist ubyte数据集的类。load_images用于读取训练数据或测试数据,load_labels用于读取标签数据。ubyte数据解析代码主要参考这个博客。
  • mnist.py: load_train_images用于读取并返回训练数据和标签数据,load_test_images用于读取并返回测试数据和标签数据。train函数用于训练,predict读取的是一个图片,用于预测一个28x28的手写字符数据,predict_by_matrix读取的是一个size为1 x 784的矩阵,并返回一个预测值。

本次实验学习总结

本次实验是对A1项目的改进,在A1项目中,我们是使用numpy手写神经网络模型,通过A1项目我对神经网络有了细致的了解,而本次项目是使用神经网络学习框架Tensorflow来完成的,框架的学习有时候并不是一件简单的事,在我看来Tensorflow框架的学习并不比python的学习轻松,你需要了解其中的内部机制,需要了解它的API的使用等等。好在Tensorflow官方教程详细,而且Tensorflow的中文教程也比较丰富,这才使得本次项目能够顺利完成。

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

项目介绍

通过使用深度学习框架根据血常规检验的各项数据预测年龄和性别。本次项目的代码地址在这里.

运行环境

# 安装numpy,
sudo apt-get install python-numpy # http://www.numpy.org/
# 安装opencv
sudo apt-get install python-opencv # http://opencv.org/

##安装OCR和预处理相关依赖
sudo apt-get install tesseract-ocr
sudo pip install pytesseract
sudo apt-get install python-tk
sudo pip install pillow

# 安装Flask框架、mongo
sudo pip install Flask
sudo apt-get install mongodb # 如果找不到可以先sudo apt-get update
sudo service mongodb started
sudo pip install pymongo

运行

cd  BloodTestReportOCR
python view.py # upload图像,在浏览器打开http://yourip:8080

view.py

Web 端上传图片到服务器,存入mongodb并获取oid,稍作修整,希望能往REST架构设计,目前还不完善;
前端采用了vue.js, mvvm模式。写了两个版本,一个是index.html无插件,另一个使用了bootstrap-fileinput插件,有点问题;

imageFilter.py

对图像透视裁剪和OCR进行了简单的封装,以便于模块间的交互,规定适当的接口

    imageFilter = ImageFilter() # 可以传入一个opencv格式打开的图片

    num = 22
    print imageFilter.ocr(num)

ocr函数 - 模块主函数返回识别数据

用于对img进行ocr识别,他会先进行剪切,之后进一步做ocr识别,返回一个json对象
如果剪切失败,则返回None
@num 规定剪切项目数

perspect函数做 - 初步的矫正图片

用于透视image,他会缓存一个透视后的opencv numpy矩阵,并返回该矩阵
透视失败,则会返回None,并打印不是报告
@param 透视参数

  • 关于param

参数的形式为[p1, p2, p3 ,p4 ,p5]。
p1,p2,p3,p4,p5都是整型,其中p1必须是奇数。

p1是高斯模糊的参数,p2和p3是canny边缘检测的高低阈值,p4和p5是和筛选有关的乘数。

如果化验报告单放在桌子上时,有的边缘会稍微翘起,产生比较明显的阴影,这种阴影有可能被识别出来,导致定位失败。
解决的方法是调整p2和p3,来将阴影线筛选掉。但是如果将p2和p3调的比较高,就会导致其他图里的黑线也被筛选掉了。
参数的选择是一个问题。
我在getinfo.default中设置的是一个较低的阈值,p2=70,p3=30,这个阈值不会屏蔽阴影线。
如果改为p2=70,p3=50则可以屏蔽,但是会导致其他图片识别困难。

就现在来看,得到较好结果的前提主要有三个
- 化验单尽量平整
- 图片中应该包含全部的三条黑线
- 图片尽量不要包含化验单的边缘,如果有的话,请尽量避开有阴影的边缘。

filter函数 - 过滤掉不合格的或非报告图片

返回img经过透视过后的PIL格式的Image对象,如果缓存中有PerspectivImg则直接使用,没有先进行透视
过滤失败则返回None
@param filter参数

autocut函数 - 将图片中性别、年龄、日期和各项目名称数据分别剪切出来

用于剪切ImageFilter中的img成员,剪切之后临时图片保存在out_path,
如果剪切失败,返回-1,成功返回0
@num 剪切项目数
@param 剪切参数

剪切出来的图片在BloodTestReportOCR/temp_pics/ 文件夹下

函数输出为data0.jpg,data1.jpg……等一系列图片,分别是白细胞计数,中性粒细胞记数等的数值的图片。

classifier.py

用于判定裁剪矫正后的报告和裁剪出检测项目的编号

imgproc.py

将识别的图像进行处理二值化等操作,提高识别率
包括对中文和数字的处理

digits

将该文件替换Tesseract-OCR\tessdata\configs中的digits

截图

网络程序设计总结 柳晓鹏_第3张图片
网络程序设计总结 柳晓鹏_第4张图片
网络程序设计总结 柳晓鹏_第5张图片

本次实验总结

我原本使用Tensorflow写了个4层的用于预测性别的DNN模型,但是由于没有normalization(归一化),导致参数变为过大NAN,正确率也在50%左右(相当于没有预测,Orz)。后来通过查找相关资料和同学的讲解才知道原来应该在输入和输出以及隐藏层normalization的。后来在班里的大神们的不懈努力下,本次实验才完美地完成了。

学习心得总结 

  1. 通过这门课我了解了神经网络的一些基本知识,比如前向反馈算法和BP反向传播算法,也熟悉了深度学习框架tensorflow的使用。 
  2. 由于该课主要的的编码语言是python,我也熟悉了python并了解了python的web开发框架flask的使用。
  3. 通过使用coding网上的源代码版本控制工具git,我熟悉了git命令的使用,也意识到一个大的工程成功离不开多人的努力。
  4. 最后就是关于markdown的使用了,我原先也是没怎么用markdown的(真的是太失败了-_-),后来孟宁老师让我把代码pull到项目主页上去,然后我就学习了markdown语法,最后发现markdown真的很方便,大大增加我写笔记的热情。

你可能感兴趣的:(网络程序设计总结 柳晓鹏)