目录
一、序章:为什么你需要掌握手搓神经网络
二、 基础篇:神经网络基础知识回顾
三、 实战配置:搭建3层神经网络的步骤与技巧
(一)、__init__ 方法初始化神经网络
(二)、train 方法实现了前向传播和反向传播
(三)、query 方法
(四)、全部代码
四、总结
一、序章:为什么你需要掌握手搓神经网络
在深度学习领域,理解神经网络内部的工作原理对于优化模型至关重要。通过手动构建一个简单的神经网络,你可以更好地了解以下概念:
- 如何计算前向传播和反向传播
- 激活函数的作用
- 损失函数和优化器如何影响模型训练
- 如何使用正则化和dropout来防止过拟合
二、 基础篇:神经网络基础知识回顾
在开始之前,让我们快速回顾一下一些基本的概念:
- 神经元 - 一个神经元接收输入信号,并通过一个激活函数产生输出。
- 权重和偏置 - 权重是连接输入到输出的参数,偏置是每个神经元的额外可调参数。
- 前向传播 - 数据流经网络的过程。
- 损失函数 - 用来衡量模型预测结果与实际结果之间的差距。
- 反向传播 - 计算损失函数相对于每个权重的梯度,以便更新权重。
三、 实战配置:搭建3层神经网络的步骤与技巧
(一)、__init__
方法初始化神经网络
1.初始化参数:
inputnodes
: 输入层节点的数量。hiddennodes
: 隐藏层节点的数量。outputnodes
: 输出层节点的数量。learningrate
: 学习率,用于调整权重更新的速度。
2.实时变量赋值:
self.inputnodes
: 设置神经网络的输入节点数量。self.hiddennodes
: 设置神经网络的隐藏节点数量。self.outputnodes
: 设置神经网络的输出节点数量。self.learningrate
: 设置神经网络的学习率。
3.权重初始化:
self.wih
: 输入层到隐藏层的权重矩阵。它是一个二维数组,形状为(hiddennodes, inputnodes)
。权重使用正态分布进行初始化,均值为 0.0,标准差为隐藏层节点数的倒数平方根。self.who
: 隐藏层到输出层的权重矩阵。它也是一个二维数组,形状为(outputnodes, hiddennodes)
。权重同样使用正态分布进行初始化,均值为 0.0,标准差为输出层节点数的倒数平方根。
4.激活函数定义:
self.activation_function
: 定义神经网络的激活函数。这里使用了 Sigmoid 函数,它是通过scipy.special.expit
实现的。- Sigmoid 函数的数学表达式为
5.学习率赋值:
-
self.lr
: 将学习率赋值给self.lr
,方便后续使用。
6.权重初始化:
- 使用正态分布初始化权重是为了避免权重过大或过小而导致的梯度消失或梯度爆炸问题。
- 标准差的选择是基于节点数的倒数平方根,这是基于经验的一种选择,它可以帮助控制权重的大小,确保网络的稳定性。
7.激活函数:
- Sigmoid 函数在早期的神经网络中非常流行,因为它可以将任意实数值映射到 0 和 1 之间,这有利于输出层的解释。然而,在深层网络中,Sigmoid 函数可能导致梯度消失问题。
import numpy as np
import scipy.special #阈值S函数的库
import matplotlib.pyplotclass NeuralNetwork:#初始化def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):self.inputnodes = inputnodes #输入节点数量self.hiddennodes = hiddennodes #隐藏节点数量self.outputnodes = outputnodes #输出节点数量self.learingrate = learningrate #学习率# 随机函数的定义权重,wih:第一层输入层至隐藏层的权重,who:隐藏层至输出层的权重# self.wih = (np.random.rand(self.hnodes, self.inodes) - 0.5)# self.who = (np.random.rand(self.onodes, self.hnodes) - 0.5)# 正太分布式的定义权重,wih:第一层输入层至隐藏层的权重,who:隐藏层至输出层的权重self.wih = np.random.normal(0.0, pow(self.hiddennodes, -0.5), (self.hiddennodes, self.inputnodes))self.who = np.random.normal(0.0, pow(self.outputnodes, -0.5), (self.outputnodes, self.hiddennodes))self.activation_function = lambda x: scipy.special.expit(x) # S阈值函数的创建# 学习率self.lr = learningratepass
(二)、train
方法实现了前向传播和反向传播
1.参数解析:
inputs_list
: 输入数据列表。targets_list
: 目标输出列表。
2.输入和目标数据转换:
inputs
: 将输入数据列表转换为一个二维数组,并转置(使用.T
),使其成为形状为(inputnodes, 1)
的列向量。targets
: 将目标输出列表转换为一个二维数组,并转置,使其成为形状为(outputnodes, 1)
的列向量。
3.前向传播:
- 隐藏层输入:
hidden_inputs
: 输入层到隐藏层的加权和。
- 隐藏层输出:
hidden_outputs
: 隐藏层的输出,使用 Sigmoid 激活函数。
4.输出层输入:
final_inputs
: 隐藏层到输出层的加权和。
5.输出层输出:
final_outputs
: 输出层的输出,同样使用 Sigmoid 激活函数。
6.计算误差:
- 输出层误差:
output_errors
: 输出层的预测误差,即目标值与输出值之差。
- 隐藏层误差:
hidden_errors
: 隐藏层的误差,是输出层误差经过隐藏层到输出层的权重矩阵转置后的乘积。
7.权重更新:
- 隐藏层到输出层权重更新:
self.who
: 更新隐藏层到输出层的权重。
- 输入层到隐藏层权重更新:
self.wih
: 更新输入层到隐藏层的权重。
#训练def train(self, inputs_list, targets_list): # 输入层 = 初始化创建一个2维数组inputs = np.array(inputs_list, ndmin=2).T#目标答案targets = np.array(targets_list, ndmin=2).T# 获得隐藏层的输入矩阵 乘积 (隐藏层的权重与输入层多维数据的乘积)hidden_inputs = np.dot(self.wih, inputs)# 隐藏层的输出矩阵,利用S阈值函数,将输入层数据放入隐藏层中hidden_outputs = self.activation_function(hidden_inputs)# 获取最终层输入矩阵 乘积(最终层的权重与隐藏层出入的多维矩阵)final_inputs = np.dot(self.who, hidden_outputs)# 获取最终层输出矩阵 利用S阈值函数final_outputs = self.activation_function(final_inputs)# 计算最终误差;目标值 - 最终输出值output_errors = targets - final_outputs# 计算隐藏层的误差:权重与最终输出层的误差的乘积hidden_errors = np.dot(self.who.T, output_errors)# 修改隐藏层至输出层的权重self.who += self.lr * np.dot((output_errors * final_outputs * (1.0 - final_outputs)),np.transpose(hidden_outputs))# 修改输入层至隐藏层的权重self.wih += self.lr * np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), np.transpose(inputs))pass
(三)、query
方法
query
方法实现了前向传播过程,用于在给定输入时预测输出。
#查询结果def query(self, inputs_list): # 创建输入层多维数据inputs = np.array(inputs_list, ndmin=2).T# 获得隐藏层的输入矩阵 乘积 (隐藏层的权重与输入层多维数据的乘积)hidden_inputs = np.dot(self.wih, inputs)# 隐藏层的输出矩阵,利用S阈值函数,将输入层数据放入隐藏层中hidden_outputs = self.activation_function(hidden_inputs)# 获取最终层输入矩阵 乘积(最终层的权重与隐藏层出入的多维矩阵)final_inputs = np.dot(self.who, hidden_outputs)# 获取最终层输出矩阵 利用S阈值函数final_outputs = self.activation_function(final_inputs)return final_outputspassdef get_weights(self):pass
(四)、全部代码
import numpy as np
import scipy.special #阈值S函数的库
import matplotlib.pyplotclass NeuralNetwork:#初始化def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):self.inputnodes = inputnodes #输入节点数量self.hiddennodes = hiddennodes #隐藏节点数量self.outputnodes = outputnodes #输出节点数量self.learingrate = learningrate #学习率# 随机函数的定义权重,wih:第一层输入层至隐藏层的权重,who:隐藏层至输出层的权重# self.wih = (np.random.rand(self.hnodes, self.inodes) - 0.5)# self.who = (np.random.rand(self.onodes, self.hnodes) - 0.5)# 正太分布式的定义权重,wih:第一层输入层至隐藏层的权重,who:隐藏层至输出层的权重self.wih = np.random.normal(0.0, pow(self.hiddennodes, -0.5), (self.hiddennodes, self.inputnodes))self.who = np.random.normal(0.0, pow(self.outputnodes, -0.5), (self.outputnodes, self.hiddennodes))self.activation_function = lambda x: scipy.special.expit(x) # S阈值函数的创建# 学习率self.lr = learningratepass
#训练def train(self, inputs_list, targets_list): # 输入层 = 初始化创建一个2维数组inputs = np.array(inputs_list, ndmin=2).T#目标答案targets = np.array(targets_list, ndmin=2).T# 获得隐藏层的输入矩阵 乘积 (隐藏层的权重与输入层多维数据的乘积)hidden_inputs = np.dot(self.wih, inputs)# 隐藏层的输出矩阵,利用S阈值函数,将输入层数据放入隐藏层中hidden_outputs = self.activation_function(hidden_inputs)# 获取最终层输入矩阵 乘积(最终层的权重与隐藏层出入的多维矩阵)final_inputs = np.dot(self.who, hidden_outputs)# 获取最终层输出矩阵 利用S阈值函数final_outputs = self.activation_function(final_inputs)# 计算最终误差;目标值 - 最终输出值output_errors = targets - final_outputs# 计算隐藏层的误差:权重与最终输出层的误差的乘积hidden_errors = np.dot(self.who.T, output_errors)# 修改隐藏层至输出层的权重self.who += self.lr * np.dot((output_errors * final_outputs * (1.0 - final_outputs)),np.transpose(hidden_outputs))# 修改输入层至隐藏层的权重self.wih += self.lr * np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), np.transpose(inputs))pass
#查询结果def query(self, inputs_list): # 创建输入层多维数据inputs = np.array(inputs_list, ndmin=2).T# 获得隐藏层的输入矩阵 乘积 (隐藏层的权重与输入层多维数据的乘积)hidden_inputs = np.dot(self.wih, inputs)# 隐藏层的输出矩阵,利用S阈值函数,将输入层数据放入隐藏层中hidden_outputs = self.activation_function(hidden_inputs)# 获取最终层输入矩阵 乘积(最终层的权重与隐藏层出入的多维矩阵)final_inputs = np.dot(self.who, hidden_outputs)# 获取最终层输出矩阵 利用S阈值函数final_outputs = self.activation_function(final_inputs)return final_outputspassdef get_weights(self):pass# 输入、隐藏、输出、学习率的初始化参数
# input_nodes = 3
# hidden_nodes = 3
# output_nodes = 3
# # learning rate is 0.5
# learning_rate = 0.3
# # create instance of neural network
# n = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
# #为了保证权重在-1至+1之间,故在随机函数后减去0.5已达到目的
# # x = np.random.rand(3, 3) - 0.5
# # print(x)
# # wih = np.random.normal(0.0, pow(hidden_nodes, -0.5),(hidden_nodes, input_nodes))
# # who = np.random.normal(0.0, pow(output_nodes, -0.5),(output_nodes, hidden_nodes))
# # print(wih)
# # print(who)
# out = n.query([1.0,0.5,-1.5])
# print(out)# number of input, hidden and output nodes
input_nodes = 784
hidden_nodes = 100
output_nodes = 10
# learning rate is 0.3
learning_rate = 0.5
#创建训练实体类
n = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)# 读取文件
filename = r"F:\BaiduNetdiskDownload\mnist_train.csv"
# 使用 loadtxt 读取文件,忽略第一列(标签),并将剩余列转换为整数
data = ((np.loadtxt(filename, delimiter=',', skiprows=0, usecols=range(1, 785), dtype=int)/255.0) * 0.99) + 0.01
data_num = np.loadtxt(filename, delimiter=',', usecols=(0,), dtype=int)
# 多次训练
for i in range(5):y =0for record in data:#默认数据为0.01targets = np.zeros(output_nodes) + 0.01#标记目标结果数据targets[int(data_num[y])] = 0.99y = y+1n.train(record, targets)pass# 读取文件
fileTestname = r"F:\BaiduNetdiskDownload\mnist_test.csv"
# 使用 loadtxt 读取文件,忽略第一列(标签),并将剩余列转换为整数
data_test = ((np.loadtxt(fileTestname, delimiter=',', skiprows=0, usecols=range(1, 785), dtype=int)/255.0) * 0.99) + 0.01
data_test_num = np.loadtxt(fileTestname, delimiter=',', usecols=(0,), dtype=int)
# test_data_file = open("mnist_dataset/mnist_test.csv", 'r')
# test_data_list = test_data_file.readlines()
# test_data_file.close()
# test the neural network
# scorecard for how well the network performs, initially empty
scorecard = []
# go through all the records in the test data set
i = 0
for record in data_test:correct_label = int(data_test_num[i])i = i+1# scale and shift the inputsinputs = record# query the networkoutputs = n.query(inputs)# the index of the highest value corresponds to the labellabel = np.argmax(outputs)# append correct or incorrect to listif label == correct_label:# network's answer matches correct answer, add 1 toscorecard.append(1)else:# network's answer doesn't match correct answer, add 0 toscorecard.append(0)
pass
pass
# calculate the performance score, the fraction of correct answers
scorecard_array = np.asarray(scorecard)
print("performance = ", scorecard_array.sum()/scorecard_array.size)
四、总结
机器学习是一门综合性的学科,不仅要学习线性代数、微积分、统计学等数学概念。还要学习编程语言例如Python等。而对于初学者来说,建议不要之间去看复杂的数据公式,因为数学公式的复杂性,很难再短时间内掌握,这样会直接打击自己的学习积极性。最终导致放弃学习机器学习。初学者应该先从最简单的概念和数学公式来了解和熟悉机器学习的方法的主要逻辑。特别是要选择一本通俗易懂的书来使自己快速了解和掌握机器学习的原理,并能动手编制一个机器学习的DEMO。来增强自己的学习信息。推荐一本博主已经看完并实践的书籍《Python神经网络编程》。这本书讲解的通俗易懂,很多数学公式都能让读者跟着作者的思路慢慢梳理出来。并且跟着作者的思路编写自己的机器学习程序。
如果看了以上博文还有些许疑惑,建议详细阅读《Python神经网络编程》进行解惑!
需要该书籍的同学可以访问以下链接获取,如无法下载或遇到什么问题可以私信博主,博主看到后会第一时间进行答复:
链接:https://pan.baidu.com/s/1ngX9yoC1HMZ2ORmHvSEtlA?pwd=0qbm
提取码:0qbm
需要训练集的同学可以访问以下链接获取:
链接:https://pan.baidu.com/s/1afPQFahKy9Ei8IjNk8o8pw?pwd=so5x
提取码:so5x