手撸神经网络(识别手写数字)

import numpy as np
import scipy.special
import matplotlib.pyplot as plt


class neuralNetwork:
    def __init__(self, inputNodes, hiddenNodes1,heddenNodes2, outputNodes, learningRate):
        # 一、设置每层节点个数和学习率
        self.inodes = inputNodes
        self.hnodes1 = hiddenNodes1
        self.hnodes2 = heddenNodes2
        self.onodes = outputNodes
        self.lr = learningRate

        # 二、初始化存储权重的矩阵
        # 1.随机初始化权重矩阵,np.random.rand(x,y)随机生成元素在-1到1的x*y的矩阵
        # self.ihWeightMatrix = np.random.rand(self.hnodes,self.inodes)-0.5 #减去0.5使范围缩小到-0.5到0.5
        # self.hoWeightMatrix = np.random.rand(self.onodes,self.hnodes)-0.5
        # numpy.random.normal(中心点的值,节点数目的-0.5次方 即与下一层节点相关的标准方差,数组形状)

        # 2.也可以以正态分布方式初始化矩阵
        self.ihWeightMatrix = np.random.normal\
            (0.0, pow(self.hnodes1, -0.5), (self.hnodes1, self.inodes))
                                                #要改成两行需要加\才可以
        self.iiWeightMatrix = np.random.normal\
            (0.0, pow(self.hnodes2, -0.5), (self.hnodes2, self.hnodes1))

        self.hoWeightMatrix = np.random.normal\
            (0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes2))

        # 三、设置激活函数sigmoid
        # lambda是匿名的 输入是传入到参数列表x的值,输出是根据表达式计算得到的值
        # scipy.special.expit(x)就是sigmoid函数,它等于1/(1+exp(-x))
        self.activateFunc = lambda x: scipy.special.expit(x)
        pass

    def train(self, inputsList, targetsList):
        # 一、先根据实际输入生成输出
        inputs = np.array(inputsList, ndmin=2).T

        hiddenInputs1 = np.dot(self.ihWeightMatrix, inputs)
        hiddenOutputs1 = self.activateFunc(hiddenInputs1)

        hiddenInputs2 = np.dot(self.iiWeightMatrix, hiddenOutputs1)
        hiddenOutputs2 = self.activateFunc(hiddenInputs2)

        finalInputs = np.dot(self.hoWeightMatrix, hiddenOutputs2)
        finalOutputs = self.activateFunc(finalInputs)

        # 二、再将实际输出与理想值比较
        targets = np.array(targetsList, ndmin=2).T
        outputErrors = targets - finalOutputs
        # 反向传播误差值:上一层的errors=WeightsMatrix的转置×这层的errors
        hiddenErrors2 = np.dot(self.hoWeightMatrix.T, outputErrors)
        hiddenErrors1 = np.dot(self.iiWeightMatrix.T, hiddenErrors2)

        # 三、根据梯度下降公式求出用于更新j层到k层之间权重的矩阵式:(•是矩阵相乘;transpose作用是使矩阵转置)
        # △W(j,k)=α×E(k)×sigmoid(O(k))×(1-sigmoid(O(k)))•(O(j)的转置)
        # 更新self.hoWeightMatrix
        self.hoWeightMatrix += self.lr * np.dot((outputErrors * finalOutputs * (1 - finalOutputs)),
                                                np.transpose(hiddenOutputs2))

        # 更新self.iiWeightMatrix
        self.iiWeightMatrix += self.lr * np.dot((hiddenErrors2 * hiddenOutputs2 * (1 - hiddenOutputs2)),
                                                np.transpose(hiddenOutputs1))

        # 更新self.ihWeightMatrix
        self.ihWeightMatrix += self.lr * np.dot((hiddenErrors1 * hiddenOutputs1 * (1 - hiddenOutputs1)),
                                                np.transpose(inputs))
        pass

    def query(self, inputsList):
        # 一、将输入的向量转置便于计算X=W(matrix)*I
        # ndmin=定义数组的最小维度 或 数组嵌套层数;“.T”表示将矩阵转置
        inputs = np.array(inputsList, ndmin=2).T

        # 二、计算隐藏层1的输入X=W(matrix)*I
        # dot可用于求数乘积、向量内积、矩阵乘法
        hiddenInputs1 = np.dot(self.ihWeightMatrix, inputs)

        # 三、隐藏层1输入通过sigmoid函数映射成输出
        hiddenOutputs1 = self.activateFunc(hiddenInputs1)

        # 四、计算隐藏层2的输入X=W(matrix)*I
        # dot可用于求数乘积、向量内积、矩阵乘法
        hiddenInputs2 = np.dot(self.iiWeightMatrix, hiddenOutputs1)

        # 五、隐藏层2输入通过sigmoid函数映射成输出
        hiddenOutputs2 = self.activateFunc(hiddenInputs2)


        # 六、计算输出层的输入
        finalInputs = np.dot(self.hoWeightMatrix, hiddenOutputs2)

        # 七、输出层输入通过sigmoid函数映射成输出
        finalOutputs = self.activateFunc(finalInputs)
        return finalOutputs

#误差分析:均方误差
def MSE(predict,fact,n):
    return np.sum((predict-fact)**2)/n

#初始化一个神经网络
input_nodes = 784
hidden_nodes1 = 30
hidden_nodes2 = 60
output_nodes = 10
learning_rate = 0.05
n = neuralNetwork(input_nodes, hidden_nodes1,hidden_nodes2,output_nodes, learning_rate)

#将训练集存入一个列表
#'r'指以只读的方式打开,mnistTrain是一个文件句柄
#读入文件中的所有行保存在trainList的列表中,列表每一项对应文件的一行字符串,可通过trainList[i]调取
mnistTrain=open("mnist_train.csv",'r')
trainList=mnistTrain.readlines()
mnistTrain.close()             #关闭文件


#开始实际训练
epochs=1  #整个训练集遍历epochs次
for i in range(epochs):
    for record in trainList:
        # 根据‘,’进行拆分
        allValues=record.split(',')
        # csv中要输入的像素点值在0~255太大,将其缩小映射到0.01~1.0方便训练(可取1但不能取0,是因为输入0值可能更新权重会失败)
        reducedInputs = (np.asfarray(allValues[1:])) / 255 * 0.99 + 0.01
        #初始化目标矩阵,用0.01和0.99而不用0和1,因为sigmoid实际输出值不可能是0或1
        targets=np.zeros(output_nodes)+0.01
        targets[int(allValues[0])]=0.99
        n.train(reducedInputs,targets)
        pass
    pass

#进行测试
mnistTest=open("mnist_test.csv",'r')
testList=mnistTest.readlines()
mnistTest.close()

score=[]    #用计分来记录准确度
sum=0.0
for record in testList:
    allValues=record.split(',')
    #期望结果
    expectedResult=int(allValues[0])
    print(expectedResult,"正确的结果")
    reducedInputs = (np.asfarray(allValues[1:])) / 255 * 0.99 + 0.01
    outputs=n.query(reducedInputs)
    # 实际输出结果
    factResult=np.argmax(outputs)   #.argmax输出outputs中的最大值
    print(factResult,"网络输出的结果\n")

    #累加计算误差平方和
    sum+=((factResult-expectedResult)**2)

    if(factResult==expectedResult):
        score.append(1)
    else:
        score.append(0)
        pass
    pass
scoreArr=np.asarray(score)
#均方差
mse=sum/len(testList)
print("accuracy:",scoreArr.sum()/scoreArr.size*100,'%')
print("均方误差MSE:",mse)



#可视化例子:
i=int(input("可视化例子请输入要测试的记录编号:"))
allValues=testList[i].split(',')
print(allValues[0])
#asfarray将文本字符串转成实数并创建数组,.reshape确保数字列表每28个元素折返一次,得到28*28矩阵
imgArr=np.asfarray(allValues[1:]).reshape((28,28))
#imshow打印成图像,cmap是颜色映射,interpolation是抗锯齿的度
plt.imshow(imgArr,cmap='Blues',interpolation='None')
plt.show()
print(n.query((np.asfarray(allValues[1:])/ 255 * 0.99) + 0.01))

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值