机器学习图像分类
近期公司在组织“人工智能”培训,本周作业是对“充气拱门”数据进行分类。上网搜了一下经典分类算法分别有LeNet-5、AlexNet、ZFNet、VGG、GoogleNet、ResNet等等。经过对比挑选了一个比较简单的AlexNet作为本次训练的模型
AlexNet
AlexNet是2012年Alex Krizhevsky提出的深度卷积神经网络模型,可以看作为LeNet-5的加强版,并且使用了ReLU、Dropout、LRN等比较新的技术点。
AlexNet包含了6亿3000万个链接,6000万个参数和65万个神经元。拥有5个卷积层,其中3个卷积层链接了最大池化,还有3个全链接层。
数据源
本次训练数据为标定好的充气拱门数据,经过裁剪缩放为227 * 227 * 3 的RGB数据
paper 地址
https://papers.nips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf
代码
模型
class AlexNet(M.Module):
def __init__(self):
super(AlexNet, self).__init__()
# 输入 227 * 227 * 3 RGB 图像,每批次为64个训练数据,shape为(64, 3, 227, 227),使用 11*11 卷积核,输出 55 * 55 * 96
# 输出尺寸计算公式 输出宽 = (输入宽 - 卷积核 + 2 * 填充) / 步长 + 1
# 代入 55 = (227 - 11 + 2 * 0) / 4 + 1
self.conv1 = M.Conv2d(3, 96, 11, stride=4)
# 使用ReLU激活
self.relu1 = M.ReLU()
# 池化,使用3 * 3 stride = 2进行池化, 输出 27 * 27 * 96
self.pool1 = M.MaxPool2d(kernel_size=3, stride=2)
# 归一化处理
self.bn1 = M.BatchNorm2d(96)
# 第二层
# 输入为第一层的55 * 55 * 96,输出27 * 27 * 256
self.conv2 = M.Conv2d(96, 256, 5, padding=2)
self.relu2 = M.ReLU()
self.pool2 = M.MaxPool2d(kernel_size=3, stride=2)
self.bn2 = M.BatchNorm2d(256)
# 第三层
# 输出 13 * 13 * 384
self.conv3 = M.Conv2d(256, 384, 3, padding=1)
self.relu3 = M.ReLU()
self.conv4 = M.Conv2d(384, 384, 3, padding=1)
self.relu4 = M.ReLU()
self.conv5 = M.Conv2d(384, 256, 3, padding=1)
self.relu5 = M.ReLU()
self.pool5 = M.MaxPool2d(kernel_size=3, stride=2)
# 输入 256 * 6 * 6 数据,输出 1 * 4096
self.fc1 = M.Linear(256 * 6 * 6, 4096)
self.relu6 = M.ReLU()
self.fc2 = M.Linear(4096, 4096)
# 丢掉部分参数
self.drop = M.Dropout(0.5)
self.relu7 = M.ReLU()
# 分类器,输出为softmax分类结果,1 * 1000
self.classifier = M.Linear(4096, 1000)
def forward(self, x):
x = self.bn1(self.pool1(self.relu1(self.conv1(x))))
x = self.bn2(self.pool2(self.relu2(self.conv2(x))))
x = self.relu3(self.conv3(x))
x = self.relu4(self.conv4(x))
x = self.pool5(self.relu5(self.conv5(x)))
# 改变数据形状(N, C, H, W)变为(N, C * H * W) 的一维数据
x = F.flatten(x, 1)
x = self.relu6(self.fc1(x))
x = self.relu7(self.drop(self.fc2(x)))
x = self.classifier(x)
return x
数据集
def resize_img(image, target_size=(224, 224), resize=False):
if resize:
return cv2.resize(image, target_size)
original_height, original_width, _ = image.shape
_h, _w = target_size
scale = _h / original_height
if scale * original_width > _w:
scale = _w / original_width
resized_height, resized_width = int(original_height * scale), int(original_width * scale)
image = cv2.resize(image, (resized_width, resized_height))
data = np.zeros((_h, _w, 3), dtype='uint8')
data[:resized_height, :resized_width, :] = image
return data
def dataset():
dataloader = []
i = 0
batch_data = []
batch_label = []
for image in images:
# 每张图rect不一样,这里写个假的。。
rect = [[0.0, 0.0], [0.9, 0.0], [0.99, 0.99], [0.0, 0.9]]
im = cv2.imread(image)
if im is not None:
w = im.shape[1]
h = im.shape[0]
x = rect[0][0]
y = rect[0][1]
x1 = rect[2][0]
y2 = rect[2][1]
# 计算裁剪位置
h1 = int(h * y)
h2 = int(h * y2)
w1 = int(w * x)
w2 = int(w * x1)
crop = im[h1:h2, w1: w2]
if crop.shape[0] > 20 and crop.shape[1] > 20:
crop = resize_img(crop, (227, 227))
# H W C 变换为 C H W
crop = crop.transpose((2, 0, 1))
batch_data.append(crop)
batch_label.append(0)
i += 1
if i % 64 == 0:
dataloader.append((np.array(batch_data), np.array(batch_label)))
batch_data = []
batch_label = []
print("dataloader success")
return dataloader
模型训练
if __name__ == '__main__':
dataloader = dataset()
net = AlexNet()
# 使用梯度下降法进行优化
optimizer = optim.SGD(
net.parameters(),
lr=0.01
)
# 初始化求导器
gm = GradManager().attach(net.parameters())
total_epochs = 20
for epoch in range(total_epochs):
total_loss = 0
batch_data = []
batch_label = []
for step, (batch_data, batch_label) in enumerate(dataloader):
with gm:
logits = net(mge.Tensor(batch_data))
# 计算loss
loss = F.loss.cross_entropy(logits, mge.Tensor(batch_label))
# 梯度计算
gm.backward(loss)
optimizer.step()
optimizer.clear_grad()
total_loss += loss.numpy().item()
print("epoch: {}, loss {}".format(epoch, total_loss))
mge.save(net.state_dict(), "net.mge")