import torch
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
'''
简单的三层全连接网络
class simpleNet(nn.module):
def __init__(self,in_dim,n_hidden_1,n_hidden_2,out_dim):
super(simpleNet,self).__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
'''
'''
nn.Sequential()
一个有序的容器,神经网络模块将按照在传入构造器的顺序依次被添加到计算图中执行,同时以神经网络模块为元素的有序字典也可以作为传入参数.
'''
'''
#增加激活函数
class Activeation_Net(nn.Module):
def __init__(self,in_dim,n_hidden_1,n_hidden_2,out_dim):
super(Activeation_Net,self).__init__()
self.layer1 = nn.Sequential(nn.Linear(in_dim,n_hidden_1),nn.ReLU(True))
#inplace=True意味着值被覆盖。数据会自动传递。
self.layer2 = nn.Sequential(nn.Linear(n_hidden_1,n_hidden_2),nn.ReLU(True))
self.layer3 = nn.Sequential(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
'''
'''
概念的引入:
Internal Covariate Shift :
其主要描述的是:训练深度网络的时候经常发生训练困难的问题,因为,每一次参数迭代更新后,
上一层网络的输出数据经过这一层网络计算后,数据的分布会发生变化,为下一层网络的学习带来困难(神经网络
本来就是要学习数据的分布,要是分布一直在变,学习就很难了),此现象称之为Internal Covariate Shift。
Covariate Shift:
Internal Covariate Shift 和Covariate Shift具有相似性,但并不是一个东西,前者发生在神经网络的内
部,所以是InternalInternal,后者发生在输入数据上。Covariate Shift主要描述的是由于训练数据和测试
数据存在分布的差异性,给网络的泛化性和训练速度带来了影响,我们经常使用的方法是做归一化或者白化。
IID独立同分布假设:
机器学习领域有个很重要的假设:IID独立同分布假设,就是假设训练数据和测试数据是满足相同分布的,这是通过
训练数据获得的模型能够在测试集获得好的效果的一个基本保障。
Batch Normalization:
BatchNorm就是在深度神经网络训练过程中使得每一层神经网络的输入保持相同分布的。
原理:
Internal Covariate Shift除了增加模型学习的难度之外,还会导致梯度消失的问题。深层神经网络在做非
线性变换前的激活输入值(就是那个x=WU+B,U是输入)随着网络深度加深或者在训练过程中,其分布逐渐发生
偏移或者变动,之所以训练收敛慢,一般是整体分布逐渐往非线性函数的取值区间的上下限两端靠近(对于
Sigmoid函数来说,意味着激活输入值WU+B是大的负值或正值),所以这导致反向传播时低层神经网络的梯度
消失,这是训练深层神经网络收敛越来越慢的本质原因。
就是说Internal Covariate Shift 主要导致两个问题:1.中间层输入分布总是变化,增加了模型拟合的难度。
2.中间层输入分布会使输出逐渐靠近激活函数梯度较小的地方,导致梯度消失。
所以解决Internal Covariate Shift 就可以提升模型的性能,BatchNorm就是通过一定的规范化手段,
把每层神经网络任意神经元这个输入值的分布强行拉回到正态分布。这样使得激活输入值落在非线性函数对输入
比较敏感的区域,这样输入的小变化就会导致损失函数较大的变化,意思是这样让梯度变大,避免梯度消失问题
产生,而且梯度变大意味着学习收敛速度快,能大大加快训练速度。
对于每个隐层神经元,把逐渐向非线性函数映射后向取值区间极限饱和区靠拢的输入分布强制拉回到比较标准的
正态分布,使得非线性变换函数的输入值落入对输入比较敏感的区域,以此避免梯度消失问题。因为梯度一直都能
保持比较大的状态,所以很明显对神经网络的参数调整效率比较高,就是变动大,就是说向损失函数最优值迈动的
步子大,也就是说收敛得快。
'''
class Batch_Net(nn.Module):
def __init__(self,in_dim,n_hidden_1,n_hidden_2,out_dim):
super(Activeation_Net,self).__init__()
self.layer1 = nn.Sequential(nn.Linear(in_dim,n_hidden_1),nn.BatchNorm1d(n_hidden_1),nn.ReLU(True))
self.layer2 = nn.Sequential(nn.Linear(n_hidden_1,n_hidden_2),nn.BatchNorm1d(n_hidden_2),nn.ReLU(True))
self.layer3 = nn.Sequential(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
batch_size = 64
learning_rate = 1e-2
num_epoches = 20
'''
把一个取值范围是[0,255]的PIL.Image或者shape为(H,W,C)的numpy.ndarray,转换成形状为[C,H,W],取值范围是[0,1.0]的torch.FloadTensor
data = np.random.randint(0, 255, size=300)
img = data.reshape(10,10,3) #更改数组样式 H、W、C 行、列、厚度
print(img.shape)
img_tensor = transforms.ToTensor()(img) # 转换成tensor
print(img_tensor)
'''
'''
transforms.Normalize(),需要传入两个参数,分别是均值和方差,进行减均值除以标准差的操作,Z-score。
给定均值:(R,G,B),标准差:(R,G,B),将会把Tensor标准化。即:Normalized_image=(image-mean)/std
transforms.Normalize([a,b,c],[d,e,f])
'''
data_tf = transforms.Compose([transforms.ToTensor(),transforms.Normalize([0.5],[0.5])])
train_dataset = datasets.MNIST(root='./data',train=True,transform=data_tf,download=True)
test_dataset = datasets.MNIST(root='./data',train=False,transform=data_tf)
train_loader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True)
test_loader = DataLoader(test_dataset,batch_size=batch_size,shuffle=False)
model = Batch_Net(28*28,300,100,10)
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(),lr=learning_rate)
model.eval()
eval_loss = 0
eval_acc = 0
for data in test_loader:
img,label = data
img = img.view(img.size(0),-1)
img = Variable(img)
label = Variable(label)
out = model(img)
loss = criterion(out,label)
eval_loss += loss.item()*label.size(0)
_,pred = torch.max(out,1)
num_correct = (pred == label).sum()
eval_acc += num_correct.item()
print('Test Loss:{:.6},Acc:{:.6}'.format(eval_loss/(len(test_dataset)),eval_acc/len(test_dataset)))