而若向之前一样两两相连的神经网络。首先输入层就有28*28=784个输入的神经元。如第一个隐藏层有30个神经元,那么两两连接,每个连接线上都对应一个weight,即有784*30个weight,另外每个神经元对应一个bias,所以有30个bias,则总共是 784*30+30=23550个参数,多了40倍参数!!!(参考该文)
串联结构的典型代表:LeNet,AlexNet,ZFNet,VGGNet
VGG16的网络模型如下图:
第一个卷积层block1_conv1:
输入 224 ∗ 224 ∗ 3 其 中 224 ∗ 224 224*224*3 其中 224*224 224∗224∗3其中224∗224为图片分辨率 3为图片的通道数
输出 224 ∗ 224 ∗ 64 其 中 224 ∗ 224 224*224*64 其中224*224 224∗224∗64其中224∗224为图片分辨率 64为卷积核数量也就是特征图数量
因此我们采用的是大小为 3 ∗ 3 ∗ 3 3*3*3 3∗3∗3的卷积核一共64个
参数数量为 3 ∗ 3 ∗ 3 ∗ 64 3*3*3*64 3∗3∗3∗64(卷积核参数)+64(bias)=1792
第一个卷积层block1_conv2:
输入 224 ∗ 224 ∗ 64 其 中 224 ∗ 224 224*224*64 其中224*224 224∗224∗64其中224∗224为图片分辨率 64为特征图数量
输出 224 ∗ 224 ∗ 64 其 中 224 ∗ 224 224*224*64 其中224*224 224∗224∗64其中224∗224为图片分辨率 64为卷积核数量也就是特征图数量
因此我们采用的是大小为 3 ∗ 3 ∗ 64 3*3*64 3∗3∗64的卷积核一共64个
参数数量为 3 ∗ 3 ∗ 64 ∗ 64 3*3*64*64 3∗3∗64∗64(卷积核参数)+64(bias)=36928
所有卷积层的参数计算以此类推,不再赘述
第一个全连接层fc1:
输入为 7 ∗ 7 ∗ 512 其 中 7 ∗ 7 7*7*512 其中7*7 7∗7∗512其中7∗7图片分辨率 512为图片的通道数
输出为4096个参数
全连接便是将输出的参数全部对应到输入的参数上去 因此参数个数为 7 ∗ 7 ∗ 512 ∗ 4096 7*7*512*4096 7∗7∗512∗4096(全连接参数)+4096(bias)=102764544
最后一个全连接层fc3,也就是上表中的prediction层:
输入为4096
输入为1000 也就是分类的类别数量
参数为 4096 ∗ 1000 ( 全 连 接 参 数 ) 4096*1000(全连接参数) 4096∗1000(全连接参数)+1000(bias)=4097000
首先,我们知道概率有两个性质:1)预测的概率为非负数;2)各种预测结果概率之和等于1。
softmax就是将在负无穷到正无穷上的预测结果按照这两步转换为概率的。
1)将预测结果转化为非负数
下图为y=exp(x)的图像,我们可以知道指数函数的值域取值范围是零到正无穷。softmax第一步就是将模型的预测结果转化到指数函数上,这样保证了概率的非负性。
为了确保各个预测结果的概率之和等于1。我们只需要将转换后的结果进行归一化处理。方法就是将转化后的结果除以所有转化后结果之和,可以理解为转化后结果占总数的百分比。这样就得到近似的概率。
下面为大家举一个例子,假如模型对一个三分类问题的预测结果为-3、1.5、2.7。我们要用softmax将模型结果转为概率。步骤如下:
1)将预测结果转化为非负数
y1 = exp(x1) = exp(-3) = 0.05
y2 = exp(x2) = exp(1.5) = 4.48
y3 = exp(x3) = exp(2.7) = 14.88
2)各种预测结果概率之和等于1
z1 = y1/(y1+y2+y3) = 0.05/(0.05+4.48+14.88) = 0.0026
z2 = y2/(y1+y2+y3) = 4.48/(0.05+4.48+14.88) = 0.2308
z3 = y3/(y1+y2+y3) = 14.88/(0.05+4.48+14.88) = 0.7666
总结一下softmax如何将多分类输出转换为概率,可以分为两步:
1)分子:通过指数函数,将实数输出映射到零到正无穷。
import torch
import torch.nn as tnn
import torchvision.datasets as dsets
import torchvision.transforms as transforms
BATCH_SIZE = 10
LEARNING_RATE = 0.01
EPOCH = 50
N_CLASSES = 25
transform = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize(mean = [ 0.485, 0.456, 0.406 ],
std = [ 0.229, 0.224, 0.225 ]),
])
trainData = dsets.ImageFolder('../data/imagenet/train', transform)
testData = dsets.ImageFolder('../data/imagenet/test', transform)
trainLoader = torch.utils.data.DataLoader(dataset=trainData, batch_size=BATCH_SIZE, shuffle=True)
testLoader = torch.utils.data.DataLoader(dataset=testData, batch_size=BATCH_SIZE, shuffle=False)
def conv_layer(chann_in, chann_out, k_size, p_size):
layer = tnn.Sequential(
tnn.Conv2d(chann_in, chann_out, kernel_size=k_size, padding=p_size),
tnn.BatchNorm2d(chann_out),
tnn.ReLU()
)
return layer
def vgg_conv_block(in_list, out_list, k_list, p_list, pooling_k, pooling_s):
layers = [ conv_layer(in_list[i], out_list[i], k_list[i], p_list[i]) for i in range(len(in_list)) ]
layers += [ tnn.MaxPool2d(kernel_size = pooling_k, stride = pooling_s)]
return tnn.Sequential(*layers)
def vgg_fc_layer(size_in, size_out):
layer = tnn.Sequential(
tnn.Linear(size_in, size_out),
tnn.BatchNorm1d(size_out),
tnn.ReLU()
)
return layer
class VGG16(tnn.Module):
def __init__(self, n_classes=1000):
super(VGG16, self).__init__()
# Conv blocks (BatchNorm + ReLU activation added in each block)
self.layer1 = vgg_conv_block([3,64], [64,64], [3,3], [1,1], 2, 2)
self.layer2 = vgg_conv_block([64,128], [128,128], [3,3], [1,1], 2, 2)
self.layer3 = vgg_conv_block([128,256,256], [256,256,256], [3,3,3], [1,1,1], 2, 2)
self.layer4 = vgg_conv_block([256,512,512], [512,512,512], [3,3,3], [1,1,1], 2, 2)
self.layer5 = vgg_conv_block([512,512,512], [512,512,512], [3,3,3], [1,1,1], 2, 2)
# FC layers
self.layer6 = vgg_fc_layer(7*7*512, 4096)
self.layer7 = vgg_fc_layer(4096, 4096)
# Final layer
self.layer8 = tnn.Linear(4096, n_classes)
def forward(self, x):
out = self.layer1(x)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
vgg16_features = self.layer5(out)
out = vgg16_features.view(out.size(0), -1)
out = self.layer6(out)
out = self.layer7(out)
out = self.layer8(out)
return vgg16_features, out
vgg16 = VGG16(n_classes=N_CLASSES)
vgg16.cuda()
# Loss, Optimizer & Scheduler
cost = tnn.CrossEntropyLoss()
optimizer = torch.optim.Adam(vgg16.parameters(), lr=LEARNING_RATE)
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer)
# Train the model
for epoch in range(EPOCH):
avg_loss = 0
cnt = 0
for images, labels in trainLoader:
images = images.cuda()
labels = labels.cuda()
# Forward + Backward + Optimize
optimizer.zero_grad()
_, outputs = vgg16(images)
loss = cost(outputs, labels)
avg_loss += loss.data
cnt += 1
print("[E: %d] loss: %f, avg_loss: %f" % (epoch, loss.data, avg_loss/cnt))
loss.backward()
optimizer.step()
scheduler.step(avg_loss)
# Test the model
vgg16.eval()
correct = 0
total = 0
for images, labels in testLoader:
images = images.cuda()
_, outputs = vgg16(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted.cpu() == labels).sum()
print(predicted, labels, correct, total)
print("avg acc: %f" % (100* correct/total))
# Save the Trained Model
torch.save(vgg16.state_dict(), 'cnn.pkl')