pytorch0.4.0 实现前向传播网络--feed forward neural network,__call__与forward函数封装

先看个简单完整的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 inputweight+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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值