从零开始制作神经网络

提出问题:手写数字的识别(这是学习神经网络的一个最最方便收集数据的分类问题了)

制作训练所需的数据集:

先来看看别人制作好的数据集,这是一个样本总量为100的数据集,图中文件用csv保存,然后最左边框住的是代表的真实数字,然后其所在的行的其他数字就是照片的像素值(结合第二张图看)

地址:GitHub - makeyourownneuralnetwork/makeyourownneuralnetwork: Code for the Make Your Own Neural Network book

 

 什么?你还不理解?好的,那我们看图说话!这是一张手写的0的图片截图,看到这些颜色介于0-255之间的像素点之后你是否恍然大悟呢?

再具体一点说,实际的图片如图所示

 制作测试所需的测试集:

 那么如何用一个数字图片得到一个像素颜色数组呢?

import imageio
import glob
import numpy
import matplotlib.pyplot
%matplotlib inline

our_own_dataset = []

for image_file_name in glob.glob('my_own_images/2828_my_own_2.png'):
    label = int(image_file_name[-5:-4])
    # load image data from png files into an array
    img_array = imageio.imread(image_file_name, as_gray=True)
    # 将28*28的二维数组转变成784*1的一维数组,然后再用255去减(数字小了方便计算)
    img_data  = 255.0 - img_array.reshape(784)
    # 将数字进行归一化处理,即修改为0.01到1.0之间的小数)
    img_data = (img_data / 255.0 * 0.99) + 0.01
    # 将这个数组对应的手写字符标签加入到数组中
    record = numpy.append(label,img_data)
    our_own_dataset.append(record)
    pass # 在循环中,使用pass语句句来跳过该次循环,在语句中只起到占位作用

matplotlib.pyplot.imshow(our_own_dataset[3][1:].reshape(28,28), cmap='Greys', interpolation='None')
print(our_own_dataset[0])

有时我们的精力有限不可能收集到足够特殊的图片,那么我们应该如何保证资料的普适性?

答:我们可以对已有的图片组图形变换处理,如旋转

import numpy
import matplotlib.pyplot
%matplotlib inline
import scipy.ndimage

data_file = open("mnist_dataset/mnist_train_100.csv", 'r')
data_list = data_file.readlines()
data_file.close()

record = 6
all_values = data_list[record].split(',') # 读取第7组数据并按照逗号分隔放入数组
scaled_input = ((numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01).reshape(28,28) # 将数据归一化后分成28*28的二维数组

matplotlib.pyplot.imshow(scaled_input, cmap='Greys', interpolation='None') # 打印数字

# rotated anticlockwise by 10 degrees(逆时针旋转10度)
inputs_plus10_img = scipy.ndimage.rotate(scaled_input, 10.0, cval=0.01, order=1, reshape=False)
# rotated clockwise by 10 degrees(顺时针旋转10度)
inputs_minus10_img = scipy.ndimage.rotate(scaled_input, -10.0, cval=0.01, order=1, reshape=False)

matplotlib.pyplot.imshow(inputs_plus10_img, cmap='Greys', interpolation='None')

matplotlib.pyplot.imshow(inputs_minus10_img, cmap='Greys', interpolation='None')

左一:原图像,左二:逆时针旋转10度,左三:顺时针旋转10度 

  

接下来我们来讲如何训练神经网络

第一步:定义神经网络(代码如下)

numpy.random.normal(loc,scale,size)

功能:产生满足正态分布的样本数据

loc:均值(期望)

scale:标准差

size:输出值的维度,如输入size=(m,n,k),则输出m*n*k个样本

lambda x: scipy.special.expit(x)

功能:又称sigmoid激活函数,用于将输入的x压缩到0到1之间

数学表示:expit(x)= 1 /(1 + exp(-x))

等价于:

def activation_function(self, x):
    return scipy.special.expit(x)

numpy.array(object,ndmin)

功能:创建一个数组

object:数组

ndmin:指定结果数组应具有的最小维数

numpy.dot(a,b)

功能:返回两个数组的点积(数量积)

a:数组a

b:数组b

hidden_errors = numpy.dot(self.who.T, output_errors) 

功能: # 求隐层误差序列

解析:例如输出层有俩个节点,隐层两层节点时

第一个式子是按照权重占比来求出隐层误差

第二个式子是忽略掉分母近似地求隐层误差

第三个式子是最终表达式推广

numpy.transpose(a)

功能:矩阵转置

a:矩阵

self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))

误差函数E=目标值和实际值之差的平方和,求偏导可知道其

节点k的输出Ok,只取决于权重Wj,k,所以可以化简为

 

Ok:即输出层的输出值,等价于sigmoid(隐层输出值*输出权值之和)

(隐层输出值*输出权值之和)对权值求导后为对应隐层输出值Oj

系数2可以去掉,因为我们只对误差的斜率方向感兴趣,这样就能使用梯度下降的方法

误差函数E对权值的导数=(误差Ei*Si*(1-Si))与(隐层输出值的转置)的数量积

使用梯度下降更新权值,斜率为正时,为了取得最小值应减小原权值;斜率为负时,为了取得最小值应增大原权值,α是学习率可以调节变化强度,确保不会超调

class neuralNetwork:
    
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        # 输入曾节点数
        self.inodes = inputnodes 
        # 隐层节点数
        self.hnodes = hiddennodes 
        # 输出层节点数
        self.onodes = outputnodes 
        # 使用正太分布N1(0,28)输出200*784个样本,用于初始化输入权值
        self.wih = numpy.random.normal(0.0, pow(self.inodes, -0.5), (self.hnodes, self.inodes))
        # 使用正太分布N2(0,14.14)输出200*10个样本,用于初始化输出权值
        self.who = numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.onodes, self.hnodes))
        # 初始化学习率
        self.lr = learningrate
        # 定义激活函数
        self.activation_function = lambda x: scipy.special.expit(x)
        pass

    
    # train the neural network
    def train(self, inputs_list, targets_list):
        # 将list转为2维数组并转置
        inputs = numpy.array(inputs_list, ndmin=2).T
        targets = numpy.array(targets_list, ndmin=2).T
        
        # 计算隐层节点输入序列
        hidden_inputs = numpy.dot(self.wih, inputs)
        # 计算隐层节点输出序列
        hidden_outputs = self.activation_function(hidden_inputs)
        
        # 计算输出层节点输入序列
        final_inputs = numpy.dot(self.who, hidden_outputs)
        # 计算输出层节点输出序列
        final_outputs = self.activation_function(final_inputs)
        
        # 计算输出层产生的预测序列与实际序列之差,求出输出层误差序列
        output_errors = targets - final_outputs
        # 求隐层误差序列
        hidden_errors = numpy.dot(self.who.T, output_errors) 
        
        # 更新输入权值,这里是“+”所以直接用了output_errors
        self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs))
        # 更新输出权值,这里是“+”所以直接用了hidden_errors
        self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), numpy.transpose(inputs))
        pass

    
    # query the neural network
    def query(self, inputs_list):
        # 将list转为2维数组并转置
        inputs = numpy.array(inputs_list, ndmin=2).T
        
        # # 计算隐层节点输入序列
        hidden_inputs = numpy.dot(self.wih, inputs)
        # # 计算隐层节点输出序列
        hidden_outputs = self.activation_function(hidden_inputs)
        
        # 计算输出层节点输入序列
        final_inputs = numpy.dot(self.who, hidden_outputs)
        # 计算输出层节点输出序列
        final_outputs = self.activation_function(final_inputs)
        
        # 返回输出结果
        return final_outputs

第二步:训练神经网络,因为是28*28的输入,所以令输入层节点数为784,隐层节点数为200,输出层节点数为10,学习率为0.1

numpy.asfarray(arr)

功能:转换为浮点型数组

arr:输入数组

[1:]

功能:获取从位置1开始后面的字符(默认首位是0)

numpy.zeros(shape)

功能:返回指定形状的零矩阵(即矩阵内元素全为0)

shape:数据尺寸,可以为5、(2,3)、(2,3,5)等,其中(2,3)代表2行3列二维矩阵

input_nodes = 784
hidden_nodes = 200
output_nodes = 10
learning_rate = 0.1

# 创建神经网络
n = neuralNetwork(input_nodes,hidden_nodes,output_nodes, learning_rate)

# 获取训练样本数据
training_data_file = open("mnist_dataset/mnist_train_100.csv", 'r')
training_data_list = training_data_file.readlines()
training_data_file.close()

# 执行训练操作,总共有100*5次数据传入神经网络进行训练
# 设置训练次数
epochs = 5 
for e in range(epochs):
    for record in training_data_list:
        # 将字符串分割为数组
        all_values = record.split(',')
        
        # 获取输入序列,将数组限制到[0.01,1]的范围内(不让有0的出现,因为0与任何数相乘都为0,会导致权值无论如何都不会变化)
        inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01

        # 初始化真实数据,因为all_values的0位置数据即为预测的真实值,所以令对应的位置值0.99,如all_values[0]=4,则targets=[0.0.1,0.01,0.01,0.01,0.99,0.01,...,0.01]
        targets = numpy.zeros(output_nodes) + 0.01
        targets[int(all_values[0])] = 0.99
        
        # 执行训练过程
        n.train(inputs, targets)
        pass
    pass

第三步:测试神经网络训练好坏,若正确率高则可投入使用,否则需要继续训练(或提高训练轮次)

numpy.argmax(a)

功能:返回数组中最大值的索引

a:数组

scorecard = []

# go through all the records in the test data set
for record in test_data_list:
    # 将字符串分割为数组
    all_values = record.split(',')
    
    # 得到真实结果
    correct_label = int(all_values[0])
    
    # 获取输入序列,将数组限制到[0.01,1]的范围内
    inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
    
    # 放入输入序列到神经网络,查询神经网络输出结果
    outputs = n.query(inputs)
    
    # 得到神经网络预测的数值
    label = numpy.argmax(outputs)
    
    # 判断预测值与实际值是否吻合
    if (label == correct_label):
        scorecard.append(1)
    else:
        scorecard.append(0)
        pass
    pass

scorecard_array = numpy.asarray(scorecard)
print ("performance = ", scorecard_array.sum() / scorecard_array.size)

 训练轮次为5时

 训练伦次为10时,准确率提高了

第四步:使用神经网络判断图片所属的数字

import imageio
import glob

our_own_dataset = []

# 获取文件夹下图片
for image_file_name in glob.glob('my_own_images/2828_my_own_?.png'):
    
    # 获取图片对应的真实数字标签
    label = int(image_file_name[-5:-4])
    
    # 将图片转换成数组
    img_array = imageio.imread(image_file_name, as_gray=True)
    
    # 将28*28的数组转换成1*784的数组,然后255减去各元素值
    img_data  = 255.0 - img_array.reshape(784)
    
    # 获取输入序列,将数组限制到[0.01,1]的范围内
    img_data = (img_data / 255.0 * 0.99) + 0.01
    
    # 拼接数据
    record = numpy.append(label,img_data)

    # 将数据放入数组
    our_own_dataset.append(record)
    pass

# 第几个图片(从0开始)
item = 2

# 获取正确的标签
correct_label = our_own_dataset[item][0]
# 获取输入序列
inputs = our_own_dataset[item][1:]

# 查询
outputs = n.query(inputs)
print (outputs)

# 获取预测数字
label = numpy.argmax(outputs)
print("network says ", label)

# 判断是否匹配成功
if (label == correct_label):
    print ("match!")
else:
    print ("no match!")
    pass

当item=2时,匹配成功

当item=3时,匹配失败

https://www.cnblogs.com/sdu20112013/p/11561278.html

这里写图片描述 

帮助理解反向传播:我们得到一个损失值:Loss=1/2*(target1-out1)+1/2*(target2-out2),Loss中变量为out1和out2,我们想让损失值变小就需要对俩者分别求偏导找到其递减区间,我们以out1为例:

对out1求偏导后为:

 进一步发现,out1的改变受到neto1的影响,所以我们再对neto1求导

 

进一步发现,neto1的改变收到w5,outh1,w6,outh2,b2的影响,我们现在只关心w5的影响,所以对w5求偏导

最总我们得到了这样链式求导的式子,即损失值关于权值的偏导 = 链式求导的结果

上述式子也可以更具体地写成

这里写图片描述

为了简化我们有

最终我们更新权值地表达式表示为

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值