神经网络学习(一)(pytorch)
pytorch实现线性回归
w = Variable(torch.randn(1),requires_grad=True) #需要求梯度
b = Variable(torch.randn(1),requires_grad=True)
def linear_model(x):
return w*x+b
def get_loss(y_,y):
return torch.mean((y_-y)**2)
loss.backward()
w.data = w.data - 1e-2 * w.grad.data #w.data表示取w的数据
b.data = b.data - 1e-2 * b.grad.data #学习率
for e in range(10) #进行10次更新
y_ = linear_model(x_train)
loss = get _loss(y,y_train)
w.grad.zero_() #归零梯度
b.grad.zero_() #归零梯度
loss.backward()
w.data = w.data - 1e-2 * w.grad.data #更新w
b.data = b.data - 1e-2 * b.grad.data #更新w
print('epoch:{},loss:{}'.formate(e,loss.data[0]))
线性模型和梯度下降
import torch
import numpy as np
from torch.autograd import Variable
torch.manual_seed(2017)
#读入数据x和y
x_train = np.array([3.3],[4.4],[5.5],[6.71][6.93],[4.168],[9.779],[6.182],dtype=np.float32)
y_train = np,array([1.7],[2.76],[2.09],[3.19],[1.649],[1.573],[3.366],
[2.596]dtype=np.float32)
#画出图像
import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(x_train,y_train,'bo')

#转换成Tensor
x_train = torch.from_numpy(x_train)
y_train = torch.from_numpy(y_train)
#定义参数w和b
w = Variable(torch.randn(1),requires_grad=True) #随机初始化
b = Variable(torch.zeros(1),rewuires_grad=True) #使用0进行初始化
#构建线性回归模型
x_train = Variable(x_train)
y_train = Variable(y_train)
def linear_model(x)
return w * x + b
y_ = linear_model(x_train)
以上步骤就定义好了迷行,在进行参数更新前,可以先看看模型的输出结果
plt.plot(x_train.data.numpy(),y_train.data.numpy(),'bo',label='real')
plt.plot(x_train.data.numpy(),y_.data.numpy(),'ro',label='estimated')
plt.legend()

#计算误差
def get_loss(y_,y):
return torch.mean((y_ - y_train) ** 2)
loss = get_loss(y_,y_train)
print(loss)
#计算w和b的梯度
loss.backward()
#查看w和b的梯度
print(w.grad)
print(b.grad)
#更新一次参数
w.data = w.data - 1e-2 * w.grad.data
b.data = b.data - 1e-2 * b.grad.data
更新完参数之后,看一下模型输出的结果
y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(),y_train.data.numpy(),'bo',label='real')
plt.plot(x_train.data.numpy(),y_.data.numpy(),'ro',label='estimated')
plt.legend()

for e in range(10) #进行10次更新
y_ = linear_model(x_train)
loss = get _loss(y,y_train)
w.grad.zero_() #归零梯度
b.grad.zero_() #归零梯度
loss.backward()
w.data = w.data - 1e-2 * w.grad.data #更新w
b.data = b.data - 1e-2 * b.grad.data #更新w
print('epoch:{},loss:{}'.formate(e,loss.data[0]))

y_ = linear_model(x_train)
plt.plot(x_train.data.numpy(),y_train.data.numpy(),'bo',label='real')
plt.plot(x_train.data.numpy(),y_.data.numpy(),'ro',label='estimated')
plt.legend()

多项式回归模型:
#定义一个多变量函数
w_target = np.array([0.5,3,2.4]) #定义参数
b_target = np.array([0.9]) #定义参数
f_des = 'y = {:.2f} + {:.2f} * x + {:.2f} * x^2 + {:.2f} * x^3.format(b_target[0],w_target[1],w_target[2]) #打印出函数的式子
print(f_des)
![]()
#画出函数曲线
x_sample = np.arange(-3,3.1,0.1)
y_sample = b_target[0] + w_target[0] * x_sample +w_target[1] * x_sample ** 2 + w_target[2] * x_sample ** 3
plt.plot(x_sample,y_sample,label='real curve')
plt.legend()

# 构建数据x和y
# x 是一个如下矩阵[x,x^2,x^3]
# y是函数的结果[y]
x_train = np.stack([x_sample ** i for i in range(1,4)],axis=1)
x_train = torch.from_numpy(x_train).float() #转换成float tensor
y_train = torch.from_numpy(y_sample).float().unsqueeze(1) #转化成float tensor
定义需要优化的参数wi
#定义参数和模型
w = Variable(torch.randn(3,1),requires_grad=True)
b = Variable(torch.zeros(1),requires_grad=True)
#将x和y转换成Variable
x_train = Variable(x_train)
y_train = Variable(y_train)
def multi_linear(x):
return torch.mm(x,w) + b
#画出之前的模型
y_pred = multi_linear(x_train)
plt.plot(x_train.data.numpy()[:,0],y_pred.data.numpy(),label='fitting curve',color='r')
plt.plot(x_train.data.numpy()[:,0],y_sample,label='real curve',color='b')
plt.legend()

#计算误差,和之前一样
loss = get_loss(y_pred,y_train)
print(loss)
#自动求导
loss.backward()
#查看一下w和b的梯度
print(w.grad)
print(b.grad)
#更新参数
w.data = w.data - 0.001 * w.grad.data
b.data = b.data - 0.001 * b.grad.data
#画出更新一次后的模型
y_pred = multi_linear(x_train)
plt.plot(x_train.data.numpy()[:,0],y_pred.data.numpy(),label='fitting curve',color='r')
plt.plot(x_train.data.numpy()[:,0],y_sample,label='real curve',color='b')
plt.legend()

for e in range(100) #进行100次更新
y_pred = multi(x_train)
loss = get _loss(y_pred,y_train)
w.grad.data.zero_()
b.grad.data.zero_()
loss.backward()
#更新参数
w.data = w.data - 0.001 * w.grad.data #更新w
b.data = b.data - 0.001 * b.grad.data #更新w
if(e + 1) % 20 == 0:
print('epoch{},loss:{:.5f}'.formate(e+1,loss.data[0]))

#画出更新之后的结果
y_pred = multi_linear(x_train)
plt.plot(x_train.data.numpy()[:,0],y_pred.data.numpy(),label='fitting curve',color='r')
plt.plot(x_train.data.numpy()[:,0],y_sample,label='real curve',color='b')
plt.legend()

Logistic回归
Logistic回归和线性回归一样,唯一不同的地方在意Logistic回归会对y作用一个logistic函数,将其变为一种概率结果。Logistic函数也成为Sigmoid函数。
公式:
![]()
图像:

将值控制在0~1内。值越小就是第一类,越大就是第二类。
回归问题VS分类问题
分类问题希望把数据集分到某一类,是一个离散问题。
回归问题是一个连续问题,比如曲线的拟合,结果是一个连续的值。
分类问题和回复问题是机器学习和深度学习的第一步,拿到任何一个问题,都需要先确定其到底是分类还是回归,然后在进行算法设计。
损失函数
Logistic回归使用了Sigmoid函数将结果变成0~1之间,对于任意输入一个数据,经过Sigmoid之后结果记为y^,标书这个数据点属于第二类的概率,那么其属于第一类的概率就是1-y^。如果数据点属于第二类,就希望y^大,属于第二类就小,损失函数:
![]()
y表示真实的label,只能取{0,1}两个值,因为y^表示经过Logistic回归预测之后的结果,是一个0~1之间的小数。如果y是0,表示该数据属于第一类,此时希望y^越小越好,上面的loss函数变成:
![]()
训练模型的时候希望最小化loss,根据loss函数的单调性,也就是最小化y^。
如果y是1,表示数据属于第二类。loss变成:
![]()
Pytorch实现
定义sigmoid函数
#定义sigmoid函数
def sigmoid(x):
return 1/(1 + np.exp(-x))
或者直接调用:
import torch.nn.function as F
F.sigmoid()
定义Logistic回归模型
w = Variable(torch.randn(2,1),requires_grad=True)
b = Variable(torch.zeros(1),requires_grad=True)
def logistic_regression(x):
return F.sigmoid(torch.mm(x,w) + b)
#定义二分类的loss函数 clamp是控制参数范围,大于括号里
def binary_loss(y_pred,y):
logits = (y * y_pred.clamp(1e-12).log() + (1 - y) * (1 - y_pred).clamp(1e-12).log()).mean()
return logits
y_pred = logistic_regression(x_data)
loss = binary_loss(y_pred,y_data)
#自动求导并更新参数
loss.backward()
w.data = w.data - 0.1 * w.grad.data
b.data = b.data - 0.1 * b.grad.data
简便的更新参数:
用 pytorch中带的优化器,Optimizer更新。
torch.optim
#使用torch.optim更新参数
from torch import nn
w = nn.Parameter(torch.randn(2,1))
b = nn.Parameter(torch.zeros(1))
def logistic_regression(x):
return F.sigmoid(torch.mm(x,w) + b)
optimizer = torch.optim.SGD(w,b],lr=1.)
optimizer.zero_grad() #使用优化器将梯度归为0
loss.backward() #对参数求梯度
optimizer.step() #使用优化器来更新参数
注:①Parameter和Variable一样,只是Parameter是Variable的一个子类,默认需要求梯度,能够自动注册到pytorch中的model中。
②SGD表示随机梯度下降。
#进行1000次更新
for e in range(1000):
y_pred = logistic_regression(x_data)
loss = binary_loss(y_pred,y_data) #计算loss
optimizer.zero_grad() #使用优化器将梯度归为0
loss.backward() #对参数求梯度
optimizer.step() #使用优化器来更新参数
更多的loss
#使用自带的loss
criterion = nn.BCEWithLogitsLoss() #将sigmoid和loss写在一层,有更快的速度,更好的稳定性
y_pred = logistic_reg(x_data)
loss = criterion(y_pred,y_data)
代码练习:
import torch
from torch.autograd import Variable
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
#设定随机种子
torch.manual_seed(2017)
我们从data.txt读入数据
读入数据点后悔根据不同的label将数据点分为红色和蓝色,并画图展示出来。
#从data.txt中读入点
with open('./data.txt','r') as f:
data_list = [i.split('\n)[0].split(',') for i in f.readlines()]
data = [(float(i[0]),float(i[1]),float(i[2])) for i in data_list]
#标准化
x0_max = max([i[0] for i in data])
x1_max = max([i[1] for i in data])
data = [(i[0]/x0_max,i[1]/x1_max,i[2]) for i in data]
x0 = list(filter(lambda x:x[-1] == 0.0,data)) #选择第一类点
x1 = list(filter(lambda x:x[-1] == 1.0,data)) #选择第二类点
plot_x0 = [i[0] for i in x0]
plot_y0 = [i[1] for i in x0]
plot_x1 = [i[0] for i in x1]
plot_y1 = [i[1] for i in x1]
plt.plot(plot_x0,plot_yo,'ro',label='x_0')
plt.plot(plot_x1,plot_y1,'ro',label='x_1')
plt.legend(loc='best')
注:split用法

接下来将数据转换成Numpy的类型,接着转换到 Tensor为之后的训练做准备。
np_data = np.array(data,dtype='float') #转换成numpy array
x_data = torch.from_numpy(np_data[:,0:2]) #转换成Tensor,大小是[100,2]
y_data = torch.from_numpy(np_data[:,-1]).unsqueeze(1) #转换成Tensor,大小是[100,1]
接下来是sigmoid函数
#定义sigmoid函数
def sigmoid(x):
return 1 / (1 +np.exp(-x))
画出Sigmoid函数,可以看到值越大,经过sigmoid函数之后越靠近1,值越小越靠近0.
#画出sigmoid的图像
plot_x = np.arange(-10,10.01,0.01)
plot_y = sigmoid(plot_x)
plt.plot(plot_x,plot_y,'r')

x_data = Variable(x_data)
y_data = Variable(y_data)
在pytorch中不需要自己写sigmoid函数,直接调用速度要比自己实现更快,稳定性更好,
通过导入torch.nn.functional来使用
import torch.nn.functional as F
#定义logistic回归模型
w = Variable(torch.randn(2,1),requires_grad=True)
b = Variable(torch.zeros(1),requires_grad=True)
def logistic_regression(x):
return F.sigmoid(torch.mm(x,w) + b)
在更新之前,可以画出分类的效果
#画出参数更新前的效果
w0 = w[0].data[0]
w1 = w[1].data[0]
b0 = b.data[0]
plot_x = np.arange(0.2,1,0.01)
plot_y = (-w0 * plot_x - b0) / w1
plt.plot(plot_x,plot_y,'g',label='cutting_line')
plt.plot(plot_x0,plot_y0,'ro',label='x_0')
plt.plot(plot_x1,plot_y1,'bo',label='x_1')
plt.legend(loc='best')

计算loss:
![]()
#计算loss
def binary_loss(y_pred,y):
logits = (y * y_pred.clamp(1e-12).log() + (1 - y) * (1 - y_pred).clamp(1e-12).log()).mean()
return -logits
y_pred = logistic_regression(x_data)
loss = binary_loss(y_pred,y_data)
print(loss)
得到loss之后,还是使用梯度下降法更新参数,这里可以用自动求导直接得到参数的导数;
#自动求导并更新参数
loss.backward()
w.data = w.data - 0.1 * w.grad.data
b.data = n.data - 0.1 * b.grad.data
#算出一次更新后的loss
y_pred = logistic_regression(x_data)
loss = binary_loss(y_pred,y_data)
print(loss)
注意:
①如果参数有100个就需要写100行来更新参数,过于繁琐,pytorch里面有封装好的函数来做此事,就是pytorch中的优化器,torch.optim
②使用torch.optim需要另外一个数据类型就是,nn.Parameter
③使用torch.optim.SGD可以使用梯度下降法来更新参数,PyTorch中的优化器有更多的优化算法。
④将参数w和b放到torch.optim.SGD中之后,说明一下学习率的大小,就可以使用optimizer.step()来更新参数了。
比如:
#使用torch.optim更新参数
from torch import nn
w = nn.Parameter(torch.randn(2,1))
b = nn.Parameter(torch.zeros(1))
def logistic_regression(x):
return F.sigmoid(torch.mm(x,w) + b)
optimizer = torch.optim.SGD([w.b],lr=1.)
#进行1000次更新
import time
start = time.time()
for e in range(1000):
#前向传播
y_pred = logistic_regression(x_data)
loss = binary_loss(y_pred,y_data) #计算loss
#反向传播
optimizer.zero_grad() #使用优化器将梯度归0
loss.backward()
optmizer.step() #使用优化器来更新参数
#计算正确率
mask = y_pred.ge(0.5).float()
acc = (mask = y_data).sum().data[0] / y_data.shape[0]
if(e + 1) % 200 == 0:
print('epoch:{},Loss:{:.5f},Acc:{:.5f}'.format(e+1,loss.data[0],acc))
during = time.time() - start
print()
print('During Time:{:.3f}s'.format(during))
使用优化器之后更新参数非常简单,只需要在自动求导之前optimizer.zero_grad()来归来0梯度,然后使用optimizer.step()来跟新参数就可以了,非常简便。
画出更新之后的结果
#画出更新之后的结果
w0 = w[0].data[0]
w1 = w[1].data[0]
b0 = b.data[0]
plot_x = np.arange(0.2,1,0.01)
plot_y = (-w0 * plot_x - b0) / w1
plt.plot(plot_x,plot_y,'g',label='cutting_line')
plt.plot(plot_x0,plot_y0,'ro',label='x_0')
plt.plot(plot_x1,plot_y1,'bo',label='x_1')
plt.legend(loc='best')

PyTorch也提供了一些loss,比如线性回归里面的loss是nn.MSE(),而Logistic回归的二分类loss在Pytorch中是nn.BCEWithLogitsLoss(),哈游很多
另外,PyTorch处于稳定性考虑,将模型的Sigmoid操作和最后的loss都合在了,nn.BCEWithLogitsLoss(),所以我们使用PyTorch自带的loss就不需要再加上Sigmoid操作了。
#使用自带的loss
criterion = nn.BEWithLogitsLoss() #将sigmoid和loss写在一层,有更快的速度更好的稳定性
w = nn.Parameter(torch.rand(2,1))
b = nn.Parameter(torch.zeros(1))
def logistic_reg(x):
return torch.mm(x,w) + b #mm是矩阵相乘
optimizer = torch.optim.SGD([w.b],1.)
y_pred = logistic_reg(x_data)
loss = criterion(y_pred,y_data)
print(loss.data)
#进行1000次更新
import time
start = time.time()
for e in range(1000):
#前向传播
y_pred = logistic_regression(x_data)
loss = binary_loss(y_pred,y_data) #计算loss
#反向传播
optimizer.zero_grad() #使用优化器将梯度归0
loss.backward()
optmizer.step() #使用优化器来更新参数
#计算正确率
mask = y_pred.ge(0.5).float()
acc = (mask = y_data).sum().data[0] / y_data.shape[0]
if(e + 1) % 200 == 0:
print('epoch:{},Loss:{:.5f},Acc:{:.5f}'.format(e+1,loss.data[0],acc))
during = time.time() - start
print()
print('During Time:{:.3f}s'.format(during))
多层神经网络
添加激活函数的原因:如果不添加激活函数就会使多层神经网络变成一层神经网络。

PyTorch实现
#定义两层神经网络的参数
w1 = nn.Parameter(torch.randn(2,4) * 0.01) #隐藏层神经元个数2
b1 = nn.Parameter(torch.zeros(4))
w2 = nn.Parameter(torch.randn(4,1) * 0.01)
b2 = nn.Parameter(torch.zeros(1))
#定义模型
def two_network(x):
x1 = torch.mm(x,w1) + b1
x1 = F.tanh(x1) #使用PyTorch自带的tanh激活函数
x2 = torch.mm(x1,w2) + b2
return x2
optimizer = torch.optim.SGD([w1,w2,b1,b2],1.)
criterion = nn.BCEWithLogitsLoss()
#训练10000次
for e in range(10000):
out = two_network(Variable(x))
loss = criterion(out,Variable(y))
optimizer.zero_grad()
loss.backward()
optimier.step()
if (e + 1) % 1000 == 0:
print('epoch:{},loss:{}.format(e+1,loss.data[0]))
PyTorch的内置nn模块
nn模块中有很多函数,上面的两层网络无需手写的那么复杂,可以用更简单的方式定义:
#nn.Sequential可以定义序列化的模型,例如上面的3层网络,可以按照顺序进行定义
seq_net = nn.Sequential(
nn.Linear(2,4), #PyTorch中的线性层,wx+b 输入是2维向量,输出是4维向量
nn.Tanh(),
nn.Linear(4,1)
)
除了简单的序列化网络构建方式Sequential之外,PyTorch提供了一种更加灵活的网络定义方式,叫做nn.Module
#Module模板
class 网络名字(nn.Module):
def_init_(self,一些定义的参数):
super(网络名字,self)._init_()
self.layer1=
nn.Linear(num_input,num_hidden)
self.layer2 = nn.Sequential(...)
#定义需要的网络层
def forward(self,x): #定义前向传播
x1 = self.layer1(x)
x2 = self.layer2(x)
x = x1 + x2
...
return x

使用第二种方式的原因:更加灵活,因为在forward里面可以定义任何你想要的的操作。层之间可以灵活调用。
#Sequential 序列模块可以通过索引访问每一层
seq_net[0] #第一层
Linear(in_features=2,out_features=4)
#打印第一层的权重
w0 = seq_net[0].weight
print(w0)

@通过parameter可以取得模型参数
param = seq_net.parameter()
#定义优化器
optim = torch.optim.SGD(param,1.)
#训练10000次
for e in range(10000):
out = seq_net(Variable(x))
loss = criterion(out,Variable(y))
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (e + 1) % 1000 == 0:
print('epoch:{},loss:{}'.format(e+1,loss.data[0]))
训练10000次loss比之前的更低了,是因为PyTorch自带的米快比我们写的更加稳定
def plot_seq(x):
out = F.sigmoid(seq_net(Variable(torch.from_numpy(x).float()))).data.numpy()
out = (out > 0.5) * 1
return out
plot_decision_bounary(lamnda x:plot_seq(x),x.numpy(),y.numpy())

多层神经网络,Sequential和Module代码练习
import torch
import numpy as np
from torch import nn
from torch.autograd import Variable
import torch.nn.functional as F
import matplotlib.pyplot as plt
%matplotlib inline
def plot_decision_boundary(model,x,y):
#Set min and max values and give it some padding
x_min, x_max = x[:,0].min() - 1,x[:,0].max() + 1
y_min, y_max = y[:,1].min() - 1,y[:,1].max() + 1
h = 0.01
#generater a gridof points with distance h between them
xx, yy = np.meshgrid(np.arange(x_min,x_max,h),np.arange(y_min,y_max,h))
#Predic the funcion value for the whole grid
Z = model(np.c_[xx.ravel(),yy.ravel()])
Z = Z.reshape(xx.shape)
#Plot the counter and training wxamples
##plt.cm.Spectral,在这的意思就是颜色会随Z的值变化
plt.contourf(xx,yy,Z,cmap=plt.cm.Spectral)
plt.ylabel('x2')
plt.xlabel('x1')
plt.scatter(x[:,0],x[:,1],c=y.reshape(-1),s=40,cmap=plt.cm.Spectral)
注意:meshgrid作用,ravel()作用,contourf作用,scater作用
这次仍然使用一个二分类问题,但是比前面的logistics回归更复杂
np.random.seed(1)
m = 400 #样本数量
N = int(m/2) #每一类的点的个数
D = 2 #维度
x = np.zeros((m,D))
y = np.zeros((m,1),dtype='uint8') #label向量,0表示红色,1表示蓝色
a = 4
for j in range(2):
ix = range(N*j,N*(j+1))
t = np.linspace(j*3.12,(j+1)*3.12,N) + np.random.randn(N)*0.2 #theta
x[ix] = np.c_[r*np.sin(t),r*np.cos(t)]
y[ix] = j
plt.scatter(x[:,0],x[:,1],c=y.reshape(-1),s=40,cmap=plt.cm.Spectral)

可以尝试用logistic回归来解决这个问题
x = torch.from_numpy(x).float()
y = torch.from_numpy(y).flaot()
w = nn.Parameter(torch.randn(2,1))
b = nn.Parameter(torch.zeros(1))
optimizer = torch.optm.SGD([w.b],1e-1)
def logistic_regrssion(x):
return torch.mm(x,w) + b
criterion = nn.BCEWithLogitsLoss()
for e in range(100):
out = logistic_regrssion(Variable(x))
loss = criterion(out,Variable(y))
loss.backward()
optimizer.step()
if(e + 1) % 20 == 0:
print('epoch:{},loss:{}'.format(e+1,loss.data[0]))
def plot_logistic(x):
x = Variable(torch.from_numpy(x).float())
out = F.sigmoid(logistic_regression(x))
out = (out > 0.5) * 1
return out.data.numpy()
plot_decision_boundary(lambda x:plot_logistic(x),x.numpy(),y.numpy())
plt.title('logistic regression')

可以看到logistic回归并不能很好的区分开这个复杂的数据集,因为它是一个线性分类器,这个时候就需要神经网络了
#定义两层神经网络的参数
w1 = nn.Parameter(torch.randn(2,4) * 0.01) #隐藏层神经元个数2
b1 = nnParameter(torch.zeros(4))
w2 = nn.parameter(torch.randn(4,1) * 0.01)
b2 = nn.Parameter(torch.zeros(1))
#定义模型
def two_network(x):
x1 = torch.mm(x,w1) + b1
x1 = F.tanh(x1) #自带
x2 = torch.mm(x1,w2) + b2
return x2
optimizer = torch.optim.SGD([w1,w2,b1,b2],1.)
criterion = nn.BCEWithLogitsLoss()
#训练1000次
for e in range(1000):
out = two_network(Variable(x))
loss = criterion(out,Variable(y))
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (e + 1) % 1000 == 0:
print('epoch:{},loss:{}'.format(e+1,loss.data[0]))
def plot_network(x):
x = Variable(torch.from_numpy(x).float())
x1 = torch.mm(x,w1) + b1
x1 = F,tanh(x1)
x2 = torch.mm(x1,w2) +b2
out = F.sigmoid(x2)
out = (out > 0.5) * 1
return out.data.numpy()
plot_decision_boundary(lambda x:plot_network(x),x.numpy(),y.numpy())
plt.title('2 layer network')
Text(0.5,1,'2 layer network')

保存模型
保存模型在PyTorch中有两种方式,一种是将模型结构和参数都保存在一起,一种是只将参数保存下来。
#将参数和模型保存在一起
torch.save(seq_net,'save_seq_net.pth')
torch.save里面的两个参数,第一个是要保存的模型,第二个是保存的路径
#读取保存的模型
seq_net1 = torch.load('save_seq_net.pth')
print(seq_net1[0].weight)

我们看到我们重新输入了模型,并且将其命名为seq_net1,并且打印了第一层的参数
下面看看第二种保存模型的方式,只保存参数而不保存模型结构
#保存模型参数
torch.save(seq_net.state_dict(),'save_seq_net_params.pth')
通过上面的方式,我们保存了模型的参数,如果要重新读入模型的参数,首先需要重新定义一次模型,接着重新读入新参数。
seq_net2 = nn.Sequential(
nn.Linear(2,4),
nn.Tanh(),
nn.Linear(4,1)
)
seq_net2.load_state_dict(torh.load('save_seq_net_params.pth'))

print(seq_net2[0].weight)

通过这种方式我们也重新读入了相同的模型,打印第一层的参数对比,发现和前面的办法是一样有这两种保存和读取模型的方法,推荐使用第二种,因为第二种可移植性更强。
class 网络名字(nn.Module):
def _init_(self,一些定义的参数):
super(网络名字,self)._init_()
self.layer1 = nn.Linear(num_input,num_hidden)
self.layer2 = nn.Sequential(...)
...
定义需要的网络层
def forward(self,x): #定义前向传播
x1 = self.layer1(x)
x2 = self.layer2(x)
x = x1 + x2
...
return x
注意:Module里面可以使用Sequential,同时Module非常灵活,具体体现在forward中。
下面照着模板实现一下上面的神经网络
class module_net(nn.Module):
def _init_(self,num_input,num_hidden,num_output):
super(module_net,self)._init_()
self.layer1 = nn.Linear(num_input,num_hidden)
slef.layer2 = nn.Tanh()
self.layer3 = nn.Linear(num_hidden,num_output)
delf forward(self,x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
return x
no_net = nodule_net(2,4,1)
#访问模型中的某曾可以直接通过名字
#第一层
l1 - mo_net.layer1
print(l1)
Linear(in_features=2,out_features=4)
#打印出第一层的权重
print(l1.weight)

#定义优化器
optim = torch.optim.SGD(mo_net.parameters(),1.)
3我们训练10000次
for e in range(10000):
out = mo_net(Variable(x))
loss = criterion(out,Variable(y))
optim.zero_grad()
loss.backward()
optim.step()
if (e + 1) % 1000 == 0:
print('epoch:{},loss:{}'.format(e+1,loss.data[0]))

#保存模型
torch.save(mo_net.state_dict(),'module_net.pth')
反向传播算法
复杂模型步骤:
1、正向求节点值
2、反向传播求梯度
3、用梯度更新节点值
4、重新回到1
优化算法:

自动求导的本质就是一个反向传播。
参数初始化
使用numpy来初始化
因为PyTorch是一个非常灵活的框架,理论上能够对所有的Tensor进行操作,所以能够通过定义新的Tensor来初始化,下面就是个例子:
import numpy as np
import torch
from torch import nn
#定义一个Sequential模型
net1 = nn.Sequential(
nn.Linear(30,40),
nn.Relu(),
nn.Linear(40,50),
nn.Relu(),
nn.Linear(50,10)
)
#访问第一层的参数
w1 = net1[0].weight
b1 = net1[0].bias
print(w1)

注意,这是一个Parameter,也就是一个特殊的Variable,我们可以访问其.data属性得到其中的数据,然后直接定义一个新的Tensor对其进行替换,我们可以使用Pytorch中的一些随机数据生成的方式,比如torch.randn,如果要使用更多Pytorch中没有随机化方式,可以使用numpy。
#定义一个Tensor直接对其进行替换
net1[0].weight.data = torch.from_numpy(np.random.uniform(3,5,size=(40,30)))
print(net1[0].weight)

可以看到这个参数的值已经被改变了,也就是说已经被定义成了我们需要的初始化方式,如果模型中某一层需要我们手动去修改,那么我们可以直接用这种方式去访问,但是更多的时候模型中相同类型的层都需要初始化成相同的方式,这个时候一种更高效的方式是使用循环去访问,比如:
for layer in net1:
if isinstance(layer,nn.Linear):#判断是否是线性层
param_shape = layer.weight.shape
layer.weight.data = torch.from_numpy(np.random.normal(0,0.5,size=param_shape)) #定义均值为0,方差为0.5的正态分布
给出以下初始化方式:

其中nj和nj+1表示改成的输入和输出数目,所以请尝试实现以下初始化方式。
对于Module的参数初始化,其实非常简单,如果想对其中的某层进行初始化,可以字节像Sequential一样对其Tendor进行重新定义,唯一不同的地方在于,若果要用循环的方式访问,需要介绍两个属性,Children和modules,例如:
class sim_net(nn.Modlule):
def _init_(self):
super(sim_net,self)._init_()
self.l1 = nn.Sequential(
nn.Linear(30,40),
nn.ReLU()
)
self.l1[0].weight.data = torch.randn(40,30)#直接对某一层初始化
self.l2 = nn.Sequential(
nn.Linear(40,50),
nn.ReLU()
)
self.l3 = nn.Sequential(
nn.Linear(50,10),
nn.ReLU()
)
def forward(self,x):
x = self.l1(x)
x = self.l2(x)
x = self.l3(x)
return x
net2 = sim_net()
#访问children
for i in net2.Children():
print(i)

#范文modules
for i in net2.modules():
print(i)

上面的例子区别:
children只会访问到模型定义的第一层,因为上面的模型中定义了3个Sequential,所以只会访问到3个Sequential,而modules会访问到最后的结构,比如上面的例子,modules不仅仅访问到了Sequential,也访问到了Sequential里面,这样对我们做初始化非常方便,比如
for layer in net2.modules():
if ininstance(layer,nn.Linear):
param_shape = layer.weight.shape
layer.weight.data = torch.from_numpy(np.random.mormal(0,0.5,size=param_shape))
这就实现了和Sequrntial相同的初始化,同样非常方便。
torch.nn.init
from torch.nn import init
print(net1[0].weight)

init.xavier_unitorm(net1[0].weight)#这就是Xavier初始化方法,PyTorch直接内置了其实现

print(net1[0].weight)

可以看到参数已经被修改了
torch.nn.init为我们提供了更多的内置的初始化方式,避免了我们重复去 实现一些相同的操作。
上面讲了两种初始化方式,其实它们的本质是一样的,就是去修改某一层参数的实际值,而torch.nn.init提供了更多成熟的深度学习相关的初始化方式,非常方便。
更多推荐



所有评论(0)