Pytorch入门与实践——神经网络工具箱

import torch as t
from torch import nn
from torch.autograd import Variable as V
from torch.nn import functional as F

from PIL import Image
from torchvision.transforms import ToTensor, ToPILImage
from matplotlib import pyplot as plt

#nn.Module
class Linear(nn.Module):    #继承nn.Module。用nn.Module实现自己的全连接层
    def __init__(self, in_features, out_features):
        super(Linear, self).__init__()  #等价于nn.Module.__init__(self)
        self.w = nn.Parameter(t.randn(in_features, out_features))
        self.b = nn.Parameter(t.randn(out_features))

    def forward(self, x):
        x = x.mm(self.w)
        return x + self.b.expand_as(x)

# layer = Linear(4,  3)
# input = V(t.randn(2, 4))
# output = layer(input)   #等价于layers.__call__(input),在__call__函数中,主要调用的是layer.forward(x)
# # print(output)
# for name, parameter in layer.named_parameters():    #Module中的可学习参数可通过named_parameters()或者parameters()返回迭代器
#     print(name, parameter)  #w and b

class Perceptron(nn.Module):    #多层感知机
    def __init__(self, in_features, hidden_features, out_features):
        nn.Module.__init__(self)
        self.layer1 = Linear(in_features, hidden_features)
        self.layer2 = Linear(hidden_features, out_features)
    def forward(self, x):
        x = self.layer1(x)
        x = t.sigmoid(x)
        return self.layer2(x)

# perceptron = Perceptron(3, 4, 1)
# for name, param in perceptron.named_parameters():
#     print(name, param.size())


##########################常用的神经网络层#############################
#图像相关层
to_tensor = ToTensor()  #img -> tensor
to_pil = ToPILImage()
cat = Image.open(r'C:\Users\45840\Pictures\Saved Pictures\cat.jpg')
# plt.imshow(cat)
# plt.show()
# print(cat)

# cat = cat.convert('L')  #转换为灰度图像
# plt.imshow(cat)
# plt.show()
input = to_tensor(cat).unsqueeze(0) #将数据伪装成batch=1的batch
# print(input.shape)
kernel = t.ones(3, 3)/-9.
kernel[1][1] = 1
conv = nn.Conv2d(1, 1, (3, 3), 1, bias=False)
conv.weight.data = kernel.view(1, 1, 3, 3)
# out = conv(V(input))      #卷积操作
# plt.imshow(to_pil(out.data.squeeze(0)))
# plt.show()

pool = nn.AvgPool2d(2, 2)   #池化层没有可学习参数,weight是固定的
# print(list(pool.parameters()))
# out = pool(V(input))
# plt.imshow(to_pil(out.data.squeeze(0)))     #池化操作
# plt.show()

input = V(t.randn(2, 3))
linear = nn.Linear(3, 4)    #全连接层
h = linear(input)
# print(h)

bn = nn.BatchNorm1d(4)
bn.weight.data = t.ones(4) * 4
bn.bias.data = t.zeros(4)
bn_out = bn(h)
# print(bn_out)
# print(bn_out.mean(0), bn_out.var(0, unbiased=False))   #使用unbiased=False,分母不减1

dropout = nn.Dropout(0.5)
o = dropout(bn_out)
# print(o)      #o的值一部分为0,一部分的值变大

#激活函数
relu = nn.ReLU(inplace=True)    #因为inplace=True,所以input自身也会改变,会把输出直接覆盖到输入中
input = V(t.randn(2, 3))
# print(input)
relu(input)     #input值被覆盖
# print(input)

#Sequential的三种写法
net1 = nn.Sequential()
net1.add_module('conv', nn.Conv2d(3, 3, 3))
net1.add_module('batchnorm', nn.BatchNorm2d(3))
net1.add_module('activation_layer', nn.ReLU())

net2 = nn.Sequential(
    nn.Conv2d(3, 3, 3),
    nn.BatchNorm2d(3),
    nn.ReLU()
)

from collections import OrderedDict
net3 = nn.Sequential(OrderedDict([
    ('conv1', nn.Conv2d(3, 3, 3)),
    ('bn', nn.BatchNorm2d(3)),
    ('relu1', nn.ReLU())
]))

# print('net1:', net1)
# print('net2:', net2)
# print('net3:', net3)
# print(net1.conv, net2[0], net3.conv1)     #可根据名字或序号取出子module

# input = V(t.rand(1, 3, 4, 4))
# output1 = net1(input)
# output2 = net2(input)
# output3 = net3(input)
# output4 = net3.relu1(net1.batchnorm(net1.conv(input)))
# print(output1, output2, output3, output4)


class MyModule(nn.Module):
    def __init__(self):
        super(MyModule, self).__init__()
        self.list = [nn.Linear(3, 4), nn.ReLU()]    #list中的子module不能被主module识别,而ModuleList中的子module可以
        self.module_list = nn.ModuleList([nn.Conv2d(3, 3, 3), nn.ReLU()])
    def forward(self):
        pass

model = MyModule()
# print(model)
# for name, param in model.named_parameters():
#     print(name, param.size())

#循环神经网络
#PASS

#损失函数,交叉熵损失CrossEntropyloss为例
# score = V(t.randn(3, 2))    #batch_size = 3
# label = V(t.Tensor([1, 0, 1])).long()   #label必须是LongTensor
# criterion = nn.CrossEntropyLoss()
# loss = criterion(score, label)
# print(loss)


##################优化器########################
#首先定义一个LeNet网络
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 6, 5),
            nn.ReLU(),
            nn.MaxPool2d(2, 2),
            nn.Conv2d(6, 16 ,5),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        self.classifier = nn.Sequential(
            nn.Linear(16*5*5, 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, 10)
        )

    def forward(self, x):
        x = self.features(x)
        x = x.view(-1, 16*5*5)
        x = self.classifier(x)
        return x

net = Net()

from torch import optim     #常用优化方法全部封装在torch.optim中

# optimizer = optim.SGD(params=net.parameters(), lr=1)
# optimizer.zero_grad()   #梯度清零,等价于net.zero_grad()
#
# input = V(t.randn(1, 3, 32, 32))
# output = net(input)
# # print(output)
# output.backward(output)     #fake backward
# optimizer.step()    #执行优化

#为不同子网络设置不同的学习率,在finetune中经常用到
# optimizer = optim.SGD([
#     {'params': net.features.parameters()},      #未指定学习率,使用默认学习率,为1e-5
#     {'params': net.classifier.parameters(), 'lr': 1e-2}
# ], lr = 1e-5)

#只为两个全连接层设置较大的学习率,其余层的学习率较小
# special_layers = nn.ModuleList([net.classifier[0], net.classifier[3]])
# special_layers_params = list(map(id, special_layers.parameters()))
# base_params = filter(lambda p: id(p) not in special_layers_params, net.parameters())
# optimizer = optim.SGD([
#     {'params': base_params},
#     {'params': special_layers.parameters(), 'lr': 0.01}
# ], lr = 0.001)

#调整学习率,新建一个optimizer
# old_lr = 0.1
# optimizer = optim.SGD([
#     {'params': net.features.parameters()},
#     {'params': net.classifier.parameters(), 'lr': old_lr*0.1}
# ], lr = 1e-5)


#################nn.functional###############
# input = V(t.randn(2, 3))
# model = nn.Linear(3, 4)
# output1 = model(input)
# output2 = nn.functional.linear(input, model.weight, model.bias)
# # print(output1 == output2)
#
# b = nn.functional.relu(input)
# b2 = nn.ReLU()(input)
# # print(b == b2)
#
# from torch.nn import functional as F
# class Net(nn.Module):
#     def __init__(self):
#         super(Net, self).__init__()
#         self.conv1 = nn.Conv2d(3, 6, 5)
#         self.conv2 = nn.Conv2d(6, 16, 5)
#         self.fc1 = nn.Linear(16*5*5, 120)
#         self.fc2 = nn.Linear(120, 84)
#         self.fc3 = nn.Linear(84, 10)
#
#     def forward(self, x):   #不具备可学习参数的层(激活层、池化层等)可以用函数代替,这样可以不用放置在构造函数__init__中
#         x = F.pool(F.relu(self.conv1(x)), 2)
#         x = F.pool(F.relu(self.conv2(x)), 2)
#         x = x.view(-1, 16*5*5)
#         x = F.relu(self.fc1(x))
#         x = F.relu(self.fc2(x))
#         x = self.fc3(x)
#         return x


####################初始化策略######################
#PyTorch中的nn.init模块专门为初始化设计,实现了常用的初始化策略
#利用nn.init初始化
# from torch.nn import init
# linear = nn.Linear(3, 4)
# t.manual_seed(1)
# init.xavier_normal(linear.weight)   #等价于linear.weight.data.normal_(0, std)

#直接初始化
# import math
# linear = nn.Linear(4, 3)
# t.manual_seed(1)
# std = math.sqrt(2)/math.sqrt(7.)    #xavier初始化的计算公式
# print(linear.weight.data.normal_(0, std))

#对模型的所有参数进行初始化
# for name, params in net.named_parameters():
#     if name.find('linear') != -1:
#         #init linear
#         params[0]   #weight
#         params[1]   #bias
#     elif name.find('conv') != -1:
#         pass
#     elif name.find('norm') != -1:
#         pass

#保存模型
# t.save(net.state_dict(), 'net.pth')
# #加载已保存的模型
# net2 = Net()
# net2.load_state_dict(t.load('net.path'))


########################50行代码搭建ResNet########################
class ResidualBlock(nn.Module):
    #实现子module:Residual Block
    def __init__(self, inchannel, outchannel, stride=1, shortcut=None):
        super(ResidualBlock, self).__init__()
        self.left = nn.Sequential(
            nn.Conv2d(inchannel, outchannel, 3, stride, 1, bias=False),
            nn.BatchNorm2d(outchannel),
            nn.ReLU(inplace=True),
            nn.Conv2d(outchannel, outchannel, 3, 1, 1, bias=False),
            nn.BatchNorm2d(outchannel)
        )
        self.right = shortcut

    def forward(self, x):
        out = self.left(x)
        residual = x if self.right is None else self.right(x)
        out += residual
        return F.relu(out)

class ResNet(nn.Module):
    def __init__(self, num_classes=1000):
        super(ResNet, self).__init__()
        #前几层图像转换
        self.pre = nn.Sequential(
            nn.Conv2d(3, 64, 7, 2, 3, bias=False),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(3, 2, 1)
        )
        # 重复的layer,分别有3, 4, 6, 3个residual block
        self.layer1 = self._make_layer(64, 128, 3)
        self.layer2 = self._make_layer(128, 256, 4, stride=2)
        self.layer3 = self._make_layer(256, 512, 6, stride=2)
        self.layer4 = self._make_layer(512, 512, 3, stride=2)

        #分类用的全连接
        self.fc = nn.Linear(512, num_classes)

    def _make_layer(self, inchannel, outchannel, block_num, stride=1):
        #构建layer,包含多个residual block
        shortcut = nn.Sequential(
            nn.Conv2d(inchannel, outchannel, 1, stride, bias=False),
            nn.BatchNorm2d(outchannel)
        )

        layers = []
        layers.append(ResidualBlock(inchannel, outchannel, stride, shortcut))
        for i in range(1, block_num):
            layers.append(ResidualBlock(outchannel, outchannel))
        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.pre(x)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        x = F.avg_pool2d(x, 7)
        x = x.view(x.size(0), -1)
        return self.fc(x)

model = ResNet()
input = V(t.randn(1, 3, 224, 224))
o = model(input)
print(o)

你可能感兴趣的:(Pytoch入门与实践)