numpy搭建两层神经网络
#用numpy搭建两层神经网络
N,D_in,H,D_out=64,1000,100,10 #输入64*1000,隐藏层为64*100,输出64*10
x=np.random.randn(N,D_in)
w1=np.random.randn(D_in,H)
w2=np.random.randn(H,D_out)
y = np.random.randn(N, D_out)
learn_rate=1e-6
for it in range(500):
#forward pass 前向传递
h=x.dot(w1)#点乘
h_relu=np.maximum(0,h)
y_pred=h_relu.dot(w2)
#compute loss,采用均方差损失函数
loss = np.square(y_pred-y).sum()
print(it,loss)
#backward pass
grad_y_pred=2.0*(y_pred-y)
grad_w2=h_relu.T.dot(grad_y_pred)
grad_h_relu=grad_y_pred.dot(w2.T)
grad_h=grad_h_relu.copy()
grad_h[h<0]=0
grad_w1=x.T.dot(grad_h)
w1 -=learn_rate*grad_w1
w2 -=learn_rate*grad_w2
结果如下
pytorch改写网络
import torch
N,D_in,H,D_out=64,1000,100,10 #输入64*1000,隐藏层为64*100,输出64*10
x=torch.randn(N,D_in)
# x=torch.randn(N,D_in).to("cuda:0")
# x=torch.randn(N,D_in).cuda()将变量传至cuda运算
w1=torch.randn(D_in,H,requires_grad=True)
w2=torch.randn(H,D_out,requires_grad=True)
y = torch.randn(N, D_out)
learn_rate=1e-6
for it in range(500):
#forward pass 前向传递
h=x.mm(w1)#点乘
h_relu=h.clamp(min=0)#相当于relu函数
y_pred=h_relu.mm(w2)
# y_pred=x.mm(w1).clamp(min=0).mm(w2)
#compute loss,采用均方差损失函数
loss = (y_pred - y).pow(2).sum()
print(it,loss.item())#将tensor转化为数值
#backward pass
loss.backward()
with torch.no_grad():#清除grad内存
w1 -=learn_rate*w1.grad
w2 -=learn_rate*w2.grad
w1.grad.zero_()
w2.grad.zero_()
由于torch和numpy其实是共用内存的,其很多语法都是共用的,不过torch处理的是向量,numpy处理的是矩阵,数值,因此代码方面没有很大的改动。而且pytorch里是有autograd的,可以自动反向求导,因此反向求导的过程就不需要我们编写代码,需要注意的是每次参数更新后,反向传播前需要清理梯度内存。结果如下:
torch.nn包搭建网络
#用torch.nn包来搭建网络
import torch
import torch.nn as nn
import torch.nn.functional as F
N,D_in,H,D_out=64,1000,100,10
x=torch.randn(N,D_in)
y=torch.randn(N,D_out)
#定义一个网络
model=nn.Sequential(
torch.nn.Linear(D_in,H),
torch.nn.ReLU(),
torch.nn.Linear(H,D_out)
)
torch.nn.init.normal_(model[0].weight)#使用normal去初始化参数,效果会好一些
torch.nn.init.normal_(model[2].weight)
loss_fn=nn.MSELoss(reduction='sum')#nn包中自带有MSE损失函数
learn_rate=1e-6
for it in range(500):
y_pred=model(x)
loss=loss_fn(y_pred,y)
print(it,loss.item())
loss.backward()#反向传播
with torch.no_grad():
for parm in model.parameters():#可看到所有网络中参数
parm -= learn_rate*parm.grad#更新参数
model.zero_grad()#清空梯度内存很重要,否则梯度会叠加
这里我们采用了torch.nn.Sequential来定义一个网络,我们看到网络调整的比较慢,这里可以采用torch.nn.init.normal_来初始化参数使得网络效果更好,当然也可以通过调整学习率来加速网络的训练。
当遇到要建立更加复杂的网络结构的问题时,则更倾向于用自定义子类torch.nn.Module来构建一个网络
import torch.nn as nn
import torch.nn.functional as F
import torch.optim
N,D_in,H,D_out=64,1000,100,10
x=torch.randn(N,D_in)
y=torch.randn(N,D_out)
#定义一个网络
class TwolayerNet(nn.Module):
def __init__(self,D_in,H,D_out):
super(TwolayerNet,self).__init__()
self.linear1=nn.Linear(D_in,H)
self.linear2=nn.Linear(H,D_out)
def forward(self,x):
y_pred=self.linear2(F.relu(self.linear1(x)))
return y_pred
model=TwolayerNet(D_in,H,D_out)
# torch.nn.init.normal_(model[0].weight)#使用normal去初始化参数,效果会好一些
# torch.nn.init.normal_(model[2].weight)
loss_fn=nn.MSELoss(reduction='sum')#nn包中自带有MSE损失函数
learn_rate=1e-4
optimizer=torch.optim.Adam(model.parameters(),lr=learn_rate)
for it in range(500):
y_pred=model(x)
loss=loss_fn(y_pred,y)
print(it,loss.item())
optimizer.zero_grad()#求导前把梯度内存清空
loss.backward()#反向传播
optimizer.step()#求导后更新参数
torch.optim更新参数
如果你想使用不同的更新规则,类似于 SGD, Nesterov-SGD, Adam,RMSProp等,可使用torch.optim
import torch.nn as nn
import torch.nn.functional as F
import torch.optim
N,D_in,H,D_out=64,1000,100,10
x=torch.randn(N,D_in)
y=torch.randn(N,D_out)
#定义一个网络
model=nn.Sequential(
torch.nn.Linear(D_in,H),
torch.nn.ReLU(),
torch.nn.Linear(H,D_out)
)
# torch.nn.init.normal_(model[0].weight)#使用normal去初始化参数,效果会好一些
# torch.nn.init.normal_(model[2].weight)
loss_fn=nn.MSELoss(reduction='sum')#nn包中自带有MSE损失函数
learn_rate=1e-4
optimizer=torch.optim.Adam(model.parameters(),lr=learn_rate)
for it in range(500):
y_pred=model(x)
loss=loss_fn(y_pred,y)
print(it,loss.item())
optimizer.zero_grad()#求导前把梯度内存清空
loss.backward()#反向传播
optimizer.step()#求导后更新参数