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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章