先看个简单完整的pytorch模型示例:
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
# Device configuration 设置是否使用cuda
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# Hyper-parameters 超参数
input_size = 784
hidden_size = 500
num_classes = 10
num_epochs = 5
batch_size = 100
learning_rate = 0.001
# 1 MNIST dataset 加载图像数据
train_dataset = torchvision.datasets.MNIST(root='../../data',
train=True,
transform=transforms.ToTensor(),
download=True)
test_dataset = torchvision.datasets.MNIST(root='../../data',
train=False,
transform=transforms.ToTensor())
# 2 Data loader pytorch的数据加载方式,tensorflow是没有的
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False)
# 3 Fully connected neural network with one hidden layer 定义网络
class NeuralNet(nn.Module):
def __init__(self, input_size, hidden_size, num_classes):
super(NeuralNet, self).__init__()
self.fc1 = nn.Linear(input_size, hidden_size)
self.relu = nn.ReLU()
self.fc2 = nn.Linear(hidden_size, num_classes)
def forward(self, x):
out = self.fc1(x)
out = self.relu(out)
out = self.fc2(out)
return out
model = NeuralNet(input_size, hidden_size, num_classes).to(device)
# 4 Loss and optimizer 定义损失和优化函数
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(),
lr=learning_rate)
# 5 Train the model 训练模型
total_step = len(train_loader)
for epoch in range(num_epochs):
for i, (images, labels) in enumerate(train_loader): # batch size的大小
# Move tensors to the configured device
images = images.reshape(-1, 28*28).to(device)
labels = labels.to(device)
# Forward pass 前向传播
outputs = model(images)
loss = criterion(outputs, labels)
# Backward and optimize 后向传播
optimizer.zero_grad()
loss.backward()
optimizer.step()
if (i+1) % 100 == 0:
print('Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}'
.format(epoch+1, num_epochs, i+1, total_step, loss.item()))
# Test the model 预测
# In test phase, we don't need to compute gradients (for memory efficiency)
with torch.no_grad():
correct = 0
total = 0
for images, labels in test_loader:
images = images.reshape(-1, 28*28).to(device)
labels = labels.to(device)
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: {} %'.format(100 * correct / total))
# Save the model checkpoint
torch.save(model.state_dict(), 'model.ckpt')
流程一般都是这样:加载数据 --》 定义网络 --》传入数据训练 --》预测
from: https://github.com/yunjey/pytorch-tutorial
框架就是框架,往往都是这样,简单可靠,使用方便,兼顾一定灵活性,而这个灵活性往往需要我们见多识广,主动去挖掘探索,深入理解框架源码的封装。比如这里的forward前向函数,这是pytorch模型(继承类)必备的一个函数(其反向传播不用咱们去操心,基本底层实现),我们可以深入研究一下为什么需要这个函数。基本原理+技巧,组成万千编程语言与框架的一切。
在此之前,咱先看看_call__函数。
要学习pytorch,一个前提是 知道python calss中的__call__
和__init__
方法.
简单的说就是:
__init__
: 类的初始化函数,类似于c++的构造函数__call___
: 使得类对象具有类似函数的功能。
__init__
比较好理解,现在主要看一下 __call__
的功能示例:
class A():
def __call__(self):
print('i can be called like a function')
a = A()
a()
out:
i can be called like a function
让我们在调用时传入参数如何?
class A():
def __call__(self, param):
print('i can called like a function')
print('掺入参数的类型是:', type(param))
a = A()
a('i')
out:
i can called like a function
掺入参数的类型是: <class ‘str’>
发现对象a的表现完全类似一个函数。
那当然也可以在__call__
里调用其他的函数啊,
在__call__
函数中调用forward
函数,并且返回调用的结果
class A():
def __call__(self, param):
print('i can called like a function')
print('传入参数的类型是:{} 值为: {}'.format(type(param), param))
res = self.forward(param)
return res
def forward(self, input_):
print('forward 函数被调用了')
print('in forward, 传入参数类型是:{} 值为: {}'.format( type(input_), input_))
return input_
a = A()
input_param = a('i')
print("对象a传入的参数是:", input_param)
out:
i can called like a function
传入参数的类型是:<class ‘str’> 值为: i
forward 函数被调用了
in forward, 传入参数类型是:<class ‘str’> 值为: i
对象a传入的参数是: i
现在我们将初始化函数__init__
也加上,来看一下:
在对象初始化时确定初始年龄,通过调用a(2)
为对象年龄增加2岁,
class A():
def __init__(self, init_age):
super().__init__()
print('我年龄是:',init_age)
self.age = init_age
def __call__(self, added_age):
res = self.forward(added_age)
return res
def forward(self, input_):
print('forward 函数被调用了')
return input_ + self.age
print('对象初始化。。。。')
a = A(10)
input_param = a(2)
print("我现在的年龄是:", input_param)
out:
对象初始化。。。。
我年龄是: 10
forward 函数被调用了
我现在的年龄是: 12
pytorch主要也是按照__call__
, __init__
,forward
三个函数实现网络层之间的架构的
这博客讲述了pytorch中具体实现:https://blog.csdn.net/dss_dssssd/article/details/82977170
原文参见:https://blog.csdn.net/xxboy61/article/details/88101192
咱再来看下一步,forward函数封装。
先看一个列子:
import torch
from torch import nn
m = nn.Linear(20, 30)input = torch.randn(128, 20)
output = m(input)
output.size
out:
torch.Size([128, 30])
刚开始看这份代码是有点迷惑的,m是类对象,而直接像函数一样调用m,m(input)
重点:
- nn.Module 是所有神经网络单元(neural network modules)的基类
- pytorch在nn.Module中,实现了
__call__
方法,而在__call__
方法中调用了forward函数。
经过以上两点。上述代码就不难理解。
接下来看一下源码:
https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/module.py
再来看一下nn.Linear
https://pytorch.org/docs/stable/_modules/torch/nn/modules/linear.html
主要看一下forward函数:
返回的是:
i
n
p
u
t
∗
w
e
i
g
h
t
+
b
i
a
s
input∗weight+bias
input∗weight+bias
的线性函数
此时再看一下这一份代码:
import torch
from torch import nn
m = nn.Linear(20, 30)
input = torch.randn(128, 20)
output = m(input)
output.size
首先创建类对象m,然后通过m(input)
实际上调用__call__(input)
,然后__call__(input)
调用
forward()
函数,最后返回计算结果为:
[
128
,
20
]
×
[
20
,
30
]
=
[
128
,
30
]
[128,20]×[20,30]=[128,30]
[128,20]×[20,30]=[128,30]
所以自己创建多层神经网络模块时,只需要在实现__init__
和forward
即可.
接下来看一个简单的三层神经网络的例子:
# define three layers
class simpleNet(nn.Module):
def __init__(self, in_dim, n_hidden_1, n_hidden_2, out_dim):
super().__init__()
self.layer1 = nn.Linear(in_dim, n_hidden_1)
self.layer2 = nn.Linear(n_hidden_1, n_hidden_2)
self.layer3 = nn.Linear(n_hidden_2, out_dim)
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
return x
以下为各层神经元个数:
输入: in_dim
第一层: n_hidden_1
第二层:n_hidden_2
第三层(输出层):out_dim
参考:https://blog.csdn.net/dss_dssssd/article/details/82977170