神经网络的典型处理如下所示:
- 定义可学习参数的网络结构(堆叠各层和层的设计);
- 数据集的制作和输入;
- 对输入进行处理(由定义的网络层进行处理),主要体现在网络的前向传播;
- 计算loss ,由Loss层计算;
- 反向传播求梯度;
- 根据梯度改变参数值,最简单的实现方式(SGD)为:
weight = weight - learning_rate * gradient
使用pytorch和auto_grad(torch自动求导功能):
import torch
#import numpy
#from torch.autograd import Variable
EPOCH = 500
LEARNING_RATE = 1e-6
N , D_in , H , D_out = 64 , 1000, 100 , 10
# N代表的是样本个数,D_in是样本的维度,H是隐藏层的维度,D_out是输出层的维度
X = torch.randn(N,D_in)
Y = torch.randn(N,D_out)
# 先用随机值初始化两层神经网络的权重
w1 = torch.randn(D_in,H,requires_grad=True)
w2 = torch.randn(H,D_out,requires_grad=True)
"""h = w1 * x
h_relu = relu(h)
y = w2 * x"""
for step in range(EPOCH):
#前向传播
h = torch.mm(X, w1) # 隐层 h = w1 * x
h_relu = h.clamp(min=0) # relu 将小于0的部分修正为0 大于0的部分则不变
y_pred = torch.mm(h_relu, w2) # 输出层 y = w2 * h_relu
# 损失函数计算误差
loss = (y_pred - Y).pow(2).sum()
if step % 100 == 0:
print('epoch: {} loss: {:.5f}'.format(step, loss.item()))#loss为(1,)的tensor
# 反向传播 计算导数
loss.backward() # 会自动计算所有参数的导数(包括W1、W2、X)
#更新参数
with torch.no_grad():
#随机梯度下降
w1 -= LEARNING_RATE * w1.grad
w2 -= LEARNING_RATE * w2.grad
#更新完梯度之后要手动置0。不置0会一直叠加
w1.grad.zero_()
w2.grad.zero_()
使用pytorch(反向传播要自己求导):
import torch
EPOCH = 500
LEARNING_RATE = 1e-6
#超参数初始化
N , D_in , H , D_out = 64 , 1000, 100 , 10 # N代表的是样本个数,D_in是样本的维度,H是隐藏层的维度,D_out是输出层的维度
#训练数据和参数初始化
X = torch.randn(N,D_in)
Y = torch.randn(N,D_out)
# 先用随机值初始化两层神经网络的权重
w1 = torch.randn(D_in,H)
w2 = torch.randn(H,D_out)
#print("X:",X,"Y:",Y,w1,w2)
for step in range(500):
#前向传播
h = X.mm(w1) #(N,H)
h_relu = h.clamp(min=0)#(N,H)
y_pred = h_relu.mm(w2) #(N,D_out)
#定义损失函数
loss = (y_pred - Y).pow(2).sum().item()
if step % 100 == 0:
print('epoch: {} loss: {:.5f}'.format(step, loss))#loss为(1,)的tensor
#反向传播
grad_y_pred = 2.0 * (y_pred - Y)#(N,D_out)
grad_w2 = h_relu.t().mm(grad_y_pred)#(H,D_out)
grad_h_relu = grad_y_pred.mm(w2.t())#(N,H)
grad_h[h > 0] = grad_h_relu[h > 0].clone()#大于0时导数不变
grad_h[h < 0] = 0#小于0时导数变0
grad_w1 = X.t().mm(grad_h)#要grad_w1的矩阵维度和w1维度相同,才能相减
#参数更新
w1 -= LEARNING_RATE * grad_w1
w2 -= LEARNING_RATE * grad_w2
这种写法,反向传播的求导是最大的难点。需要理解正向传播和反向传播来推导公式,再把公式用代码实现。最好需要看一下吴恩达的机器学习视频的前三章(代价函数和梯度求导)和神经网络部分,理解反向传播的计算方法。但是它的视频的激励函数不同,我们需要针对本代码中的relu函数来实现反向传播。
这里我贴出了手写的推导过程:
下面是对神经网络里面的一些tensor进行输出,从维度来理解
import torch
lr = 1e-6
EPOCH = 500
LEARNING_RATE = 1e-6
#超参数初始化
N , D_in , H , D_out = 3 , 4, 2 ,1 # N代表的是样本个数,D_in是样本的维度,H是隐藏层的维度,D_out是输出层的维度
#训练数据和参数初始化
X = torch.randn(N,D_in)
Y = torch.randn(N,D_out)
# 先用随机值初始化两层神经网络的权重
w1 = torch.randn(D_in,H)
w2 = torch.randn(H,D_out)
print("X:",X)#3样本 4特征
print("Y:",Y)#3结果
print("w1:",w1)
print("w2",w2)
h = X.mm(w1)
print("h:",h)
h_relu = h.clamp(min=0)#(N,h_dim)
print("h_relu",h_relu)
附上运行结果和个人理解:
X: tensor([[ 1.3393, -0.3668, -0.3531, 1.4490],
[-0.2834, 0.3056, 1.0691, 1.7448],
[ 0.1956, 0.9079, 0.4525, -1.1220]])
Y: tensor([[-1.8117],
[-0.4406],
[ 1.1850]])
w1: tensor([[ 1.4459, -0.1429],
[-0.8366, -0.5614],
[-1.4148, -0.4373],
[ 0.4911, -1.1529]])
w2 tensor([[-0.6083],
[-0.2623]])
h: tensor([[ 3.4545, -1.5016],
[-1.3210, -2.6101],
[-1.6679, 0.5581]])
h_relu tensor([[3.4545, 0.0000],
[0.0000, 0.0000],
[0.0000, 0.5581]])
- 神经网络的示意图是以一个特征(维度)为一个单元,而不是变量。
- X有三个变量,每个变量有四个特征(维度)。W1分别对四个特征的每个特征有两种权值
- 隐藏层有三个变量,两个单元(维度)
- 将变量的4种特征×权值1相加之后,就得到了隐藏层单元里这个对应变量的特征值1
- 将变量的4种特征×权值2相加之后,就得到了隐藏层单元里这个对应变量的特征值2
参考:
- 吴恩达机器学习视频:https://www.bilibili.com/video/av50747658?from=search&seid=8008773056378130405
- https://blog.csdn.net/qq_30057549/article/details/103018003
- 个人的针对吴恩达机器学习视频的笔记(下一篇推出,参考修改自黄海广博士)
最后
其实这已经是年初1月做的了, 距离现在已经很久啦。 如今想来, 这依然是入门深度学习最好的实践之一 。 入门深度学习的第一份代码,应该是这个。
然后吴恩达的机器学习视频也在上半年看完了,我是在黄海广博士的博客上稍作修改我的吴恩达机器学习笔记的。
但是后来发现,本地的markdown博客并不能在csdn上直接展示,里面的图很难传。就不更新啦,有需要可以联系我要。