从头推导与实现 BP 网络
从头推导与实现 BP 网络
回归模型
目标
学习
模型
单隐层、单节点的 BP 神经网络
策略
Mean Square Error 均方误差
模型的目标是
算法
朴素梯度下降。在每个 epoch 内,使模型对所有的训练数据都误差最小化。
网络结构
Forward Propagation Derivation
Back Propagation Derivation
模型的可学习参数为 ,更新的策略遵循感知机模型:
参数 w 的更新算法
参数 v 的更新算法
代码实现
C++ 实现
#include <iostream>
#include <cmath>
using namespace std;
class Network {
public :
Network(float eta) :eta(eta) {}
float predict(int x) { // forward propagation
this->alpha = this->v * x;
this->b = this->sigmoid(alpha);
this->beta = this->w * this->b;
float prediction = this->beta;
return prediction;
}
void step(int x, float prediction, float label) {
this->w = this->w
- this->eta
* (prediction - label)
* this->b;
this->alpha = this->v * x;
this->v = this->v
- this->eta
* (prediction - label)
* this->w
* this->sigmoid(this->alpha) * (1 - this->sigmoid(this->alpha))
* x;
}
private:
float sigmoid(float x) {return (float)1 / (1 + exp(-x));}
float v = 1, w = 1, alpha = 1, beta = 1, b = 1, prediction, eta;
};
int main() { // Going to learn the linear relationship y = 2*x
float loss, pred;
Network model(0.01);
cout << "x is " << 3 << " prediction is " << model.predict(3) << " label is " << 2*3 << endl;
for (int epoch = 0; epoch < 500; epoch++) {
loss = 0;
for (int i = 0; i < 10; i++) {
pred = model.predict(i);
loss += pow((pred - 2*i), 2) / 2;
model.step(i, pred, 2*i);
}
loss /= 10;
cout << "Epoch: " << epoch << " Loss:" << loss << endl;
}
cout << "x is " << 3 << " prediction is " << model.predict(3) << " label is " << 2*3 << endl;
return 0;
}
C++ 运行结果
初始网络权重,对数据 x=3, y=6的 预测结果为 。
训练了 500 个 epoch 以后,平均损失下降至 7.82519,对数据 x=3, y=6的 预测结果为 。
PyTorch 实现
# encoding:utf8
# 极简的神经网络,单隐层、单节点、单输入、单输出
import torch as t
import torch.nn as nn
import torch.optim as optim
class Model(nn.Module):
def __init__(self, in_dim, out_dim):
super(Model, self).__init__()
self.hidden_layer = nn.Linear(in_dim, out_dim)
def forward(self, x):
out = self.hidden_layer(x)
out = t.sigmoid(out)
return out
if __name__ == '__main__':
X, Y = [[i] for i in range(10)], [2*i for i in range(10)]
X, Y = t.Tensor(X), t.Tensor(Y)
model = Model(1, 1)
optimizer = optim.SGD(model.parameters(), lr=0.01)
criticism = nn.MSELoss(reduction='mean')
y_pred = model.forward(t.Tensor([[3]]))
print(y_pred.data)
for i in range(500):
optimizer.zero_grad()
y_pred = model.forward(X)
loss = criticism(y_pred, Y)
loss.backward()
optimizer.step()
print(loss.data)
y_pred = model.forward(t.Tensor([[3]]))
print(y_pred.data)
PyTorch 运行结果
初始网络权重,对数据 x=3, y=6的 预测结果为 。
训练了 500 个 epoch 以后,平均损失下降至 98.8590,对数据 x=3, y=6的 预测结果为 。
结论
居然手工编程的实现其学习效果比 PyTorch 的实现更好,真是奇怪!但是我估计差距就产生于学习算法的不同,PyTorch采用的是 SGD。
分类模型
目标
目标未知,因为本实验的数据集是对 iris 取前两类样本,然后把四维特征降维成两维,得到本实验的数据集。
数据简介:
-1.653443 0.198723 1 0 # 前两列为特正,最后两列“1 0”表示第一类
1.373162 -0.194633 0 1 # "0 1",第二类
模型
单隐层双输入输入节点的分类 BP 网络
策略
在整个模型的优化过程中,使得在整个训练集上交叉熵最小:
交叉熵:
算法
梯度下降,也即在每个 epoch 内,使模型对所有的训练数据都误差最小。
网络结构
如图
Forward Propagation
公式推导如下
Back Propagation
其中,
所以,
类似的,可得
代码实现
Python 3 实现
# encoding:utf8
from math import exp, log
import numpy as np
def load_data(fname):
X, Y = list(), list()
with open(fname, encoding='utf8') as f:
for line in f:
line = line.strip().split()
X.append(line[:2])
Y.append(line[2:])
return X, Y
class Network:
eta = 0.5
w = [[0.5, 0.5], [0.5, 0.5]]
b = [0.5, 0.5]
a = [0.5, 0.5]
pred = [0.5, 0.5]
def __sigmoid(self, x):
return 1 / (1 + exp(-x))
def forward(self, x):
self.a[0] = self.w[0][0] * x[0] + self.w[1][0] * x[1]
self.a[1] = self.w[0][1] * x[0] + self.w[1][1] * x[1]
self.b[0] = self.__sigmoid(self.a[0])
self.b[1] = self.__sigmoid(self.a[1])
self.pred[0] = self.__sigmoid(self.b[0]) / (self.__sigmoid(self.b[0]) + self.__sigmoid(self.b[1]))
self.pred[1] = self.__sigmoid(self.b[1]) / (self.__sigmoid(self.b[0]) + self.__sigmoid(self.b[1]))
return self.pred
def step(self, x, label):
g = (self.pred[0] - label[0]) * self.__sigmoid(self.a[0]) * (1-self.__sigmoid(self.a[0])) * x[0]
self.w[0][0] = self.w[0][0] - self.eta * g
g = (self.pred[0] - label[0]) * self.__sigmoid(self.a[0]) * (1 - self.__sigmoid(self.a[0])) * x[1]
self.w[1][0] = self.w[1][0] - self.eta * g
g = (self.pred[1] - label[1]) * self.__sigmoid(self.a[1]) * (1 - self.__sigmoid(self.a[1])) * x[0]
self.w[0][1] = self.w[0][1] - self.eta * g
g = (self.pred[1] - label[1]) * self.__sigmoid(self.a[1]) * (1 - self.__sigmoid(self.a[1])) * x[1]
self.w[1][1] = self.w[1][1] - self.eta * g
if __name__ == '__main__':
X, Y = load_data('iris.txt')
X, Y = np.array(X).astype(float), np.array(Y).astype(float)
model = Network()
pred = model.forward(X[0])
print("Label: %d %d, Pred: %f %f" % (Y[0][0], Y[0][1], pred[0], pred[1]))
epoch = 100
loss = 0
for i in range(epoch):
loss = 0
for j in range(len(X)):
pred = model.forward(X[j])
loss = loss - Y[j][0] * log(pred[0]) - Y[j][1] * log(pred[1])
model.step(X[j], Y[j])
print("Loss: %f" % (loss))
pred = model.forward(X[0])
print("Label: %d %d, Pred: %f %f" % (Y[0][0], Y[0][1], pred[0], pred[1]))
网络在训练之前,预测为:
Label: 1 0, Pred: 0.500000 0.500000
Loss: 55.430875
学习率 0.5, 训练 100 个 epoch 以后:
Label: 1 0, Pred: 0.593839 0.406161
Loss: 52.136626
结论
训练后损失减小,模型预测的趋势朝着更贴近标签的方向前进,本次实验成功。
只不过模型的参数较少,所以学习能力有限。
Reference
智慧在街市上呼喊,在宽阔处发声。
分类:
Machine Learning
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)