Учетная запись WeChat: ilulaoshi. Мой личный веб-сайт будет постоянно обновляться, нажмите, чтобы узнать подробности.Ссылка на сайт.
Накануне глубокого обучения
Хотя Янн ЛеКун предложил сверточную нейронную сеть LeNet еще в прошлом веке и использовал LeNet для классификации изображений, сверточная нейронная сеть не развивалась быстро. За почти 20 лет с тех пор, как был предложен LeNet, нейронные сети когда-то превзошли другие методы машинного обучения, такие как машины опорных векторов. Неспособность сверточных нейронных сетей быстро развиваться в то время была в основном ограничена:
1. Отсутствующие данные
Глубокое обучение требует большого количества размеченных данных, чтобы работать лучше, чем другие классические методы. Из-за ограниченного объема памяти первых компьютеров и ограниченных бюджетов на исследования в 1990-е годы большинство исследований основывалось только на небольших общедоступных наборах данных. Например, многие исследовательские работы основаны на нескольких общедоступных наборах данных, предоставленных Калифорнийским университетом в Ирвине (UCI), многие из которых содержат от нескольких сотен до нескольких тысяч изображений. Эта ситуация улучшилась с волной больших данных, которая возникла примерно в 2010 году. В частности, построение набора данных ImageNet под руководством Фейфей Ли. Набор данных ImageNet содержит 1000 категорий объектов, каждая из которых содержит до тысячи различных изображений, а общий объем данных достигает сотен гигабайт. Эта шкала не имела себе равных в других общедоступных наборах данных того времени. Кроме того, каждый год сообщество проводит конкурс под названием ImageNet Large-Scale Visual Recognition Challenge (ILSVRC), где участникам необходимо оптимизировать задачи, связанные с компьютерным зрением, на основе набора данных ImageNet. Можно сказать, что набор данных ImageNet вывел исследования компьютерного зрения и машинного обучения на новый этап.
2. Отсутствующее оборудование
Глубокое обучение очень требовательно к вычислительным ресурсам. Ограниченная вычислительная мощность раннего оборудования затрудняла обучение более сложных нейронных сетей. Однако появление графических процессоров общего назначения (GPGPU) изменило этот ландшафт. Графические процессоры давно разрабатывались для обработки изображений и компьютерных игр, особенно для высокопроизводительного матричного и векторного умножения. К счастью, математическое выражение этого похоже на выражение сверточных слоев в глубоких сетях. Концепция графических процессоров общего назначения зародилась в 2001 году с появлением таких сред программирования, как CUDA и OpenCL. Программный интерфейс CUDA не так уж сложен для начала, и ученые-исследователи могут использовать CUDA для ускорения своих научных вычислительных задач на графических процессорах NVIDIA. Примерно в 2010 году некоторые ресурсоемкие задачи начали переноситься на графические процессоры Nvidia.
Считается, что нынешний бум ИИ начался в 2012 году. В том же году Алекс Крижевский успешно обучил глубокую сверточную нейронную сеть AlexNet с использованием графического процессора NVIDIA и выиграл чемпионат в соревновании ImageNet с этой сетью, значительно повысив точность классификации изображений. В то время хранение и вычисление больших данных почти перестали быть узким местом, а предложение AlexNet также заставило академический круг и промышленность осознать потрясающую производительность глубоких нейронных сетей.
Структура сети AlexNet
Философия дизайна AlexNet и LeNet очень похожа, но есть и существенные различия.
Во-первых, по сравнению с относительно небольшим LeNet, AlexNet содержит 8 слоев преобразований с 5 сверточными и 2 полностью связанными скрытыми слоями и 1 полностью связанным выходным слоем. Ниже мы подробно опишем конструкцию этих слоев.
Форма окна свертки в первом слое AlexNet составляет 11 × 11. Поскольку подавляющее большинство изображений в ImageNet более чем в 10 раз больше по высоте и ширине, чем изображения MNIST, объекты в изображениях ImageNet занимают больше пикселей, поэтому для захвата объектов требуются большие сверточные окна. Форма сверточных окон во втором слое уменьшается до 5×5, а затем и полностью до 3×3. Кроме того, за первым, вторым и пятым сверточными слоями следует слой максимального объединения с формой окна 3 × 3 и шагом 2. Более того, количество каналов свертки, используемых AlexNet, также в десятки раз превышает количество каналов свертки в LeNet.
За последним сверточным слоем следуют два полносвязных слоя с 4096 выходами. Эти два огромных полностью связанных слоя содержат почти 1 ГБ параметров модели. Из-за ранних ограничений видеопамяти самая ранняя AlexNet использовала двухпотоковую конструкцию, так что одному графическому процессору требовалось только для обработки половины модели. К счастью, за последние несколько лет видеокарты прошли долгий путь, поэтому обычно нам больше не нужны такие специальные конструкции.
Во-вторых, AlexNet изменил сигмовидную функцию активации на более простую функцию активации ReLU. С одной стороны, расчет функции активации ReLU проще, например, в ней нет операции возведения в степень в сигмовидной функции активации. С другой стороны, функция активации ReLU упрощает обучение модели с использованием различных методов инициализации параметров. Это связано с тем, что когда выход сигмовидной функции активации очень близок к 0 или 1, градиенты в этих областях почти равны 0, так что обратное распространение не может продолжать обновлять некоторые параметры модели; в то время как градиент функции активации ReLU в положительном интервал всегда равен 1. Следовательно, если параметры модели не инициализированы должным образом, сигмовидная функция может получить почти нулевой градиент в положительном диапазоне, что сделает невозможным эффективное обучение модели.
В-третьих, AlexNet использует Dropout для управления сложностью модели полносвязного слоя, чтобы избежать переобучения. И LeNet не использует метод сброса.
В-четвертых, AlexNet вводит обширную аугментацию изображения, такую как переворачивание, обрезка и изменение цвета, чтобы еще больше увеличить набор данных, чтобы уменьшить переоснащение.
Ниже приведен немного упрощенный AlexNet, реализованный с использованием PyTorch. Эта сеть предполагает ввод 1 × 224 × 224, то есть вход имеет только один канал, например черно-белый одноцветный набор данных, такой как Fashion-MNIST.
class AlexNet(nn.Module):
def __init__(self):
super(AlexNet, self).__init__()
# convolution layer will change input shape into: floor((input_shape - kernel_size + padding + stride) / stride)
# input shape: 1 * 224 * 224
# convolution part
self.conv = nn.Sequential(
# conv layer 1
# floor((224 - 11 + 2 + 4) / 4) = floor(54.75) = 54
# conv: 1 * 224 * 224 -> 96 * 54 * 54
nn.Conv2d(in_channels=1, out_channels=96, kernel_size=11, stride=4, padding=1), nn.ReLU(),
# floor((54 - 3 + 2) / 2) = floor(26.5) = 26
# 96 * 54 * 54 -> 96 * 26 * 26
nn.MaxPool2d(kernel_size=3, stride=2),
# conv layer 2: decrease kernel size, add padding to keep input and output size same, increase channel number
# floor((26 - 5 + 4 + 1) / 1) = 26
# 96 * 26 * 26 -> 256 * 26 * 26
nn.Conv2d(in_channels=96, out_channels=256, kernel_size=5, stride=1, padding=2), nn.ReLU(),
# floor((26 - 3 + 2) / 2) = 12
# 256 * 26 * 26 -> 256 * 12 * 12
nn.MaxPool2d(kernel_size=3, stride=2),
# 3 consecutive conv layer, smaller kernel size
# floor((12 - 3 + 2 + 1) / 1) = 12
# 256 * 12 * 12 -> 384 * 12 * 12
nn.Conv2d(in_channels=256, out_channels=384, kernel_size=3, stride=1, padding=1), nn.ReLU(),
# 384 * 12 * 12 -> 384 * 12 * 12
nn.Conv2d(in_channels=384, out_channels=384, kernel_size=3, stride=1, padding=1), nn.ReLU(),
# 384 * 12 * 12 -> 256 * 12 * 12
nn.Conv2d(in_channels=384, out_channels=256, kernel_size=3, stride=1, padding=1), nn.ReLU(),
# floor((12 - 3 + 2) / 2) = 5
# 256 * 5 * 5
nn.MaxPool2d(kernel_size=3, stride=2)
)
# fully connect part
self.fc = nn.Sequential(
nn.Linear(256 * 5 * 5, 4096),
nn.ReLU(),
# Use the dropout layer to mitigate overfitting
nn.Dropout(p=0.5),
nn.Linear(4096, 4096),
nn.ReLU(),
nn.Dropout(p=0.5),
# Output layer.
# the number of classes in Fashion-MNIST is 10
nn.Linear(4096, 10),
)
def forward(self, img):
feature = self.conv(img)
output = self.fc(feature.view(img.shape[0], -1))
return output
обучение модели
Хотя AlexNet использует в статье набор данных ImageNet, поскольку время обучения набора данных ImageNet очень велико, мы используем набор данных Fashion-MNIST для демонстрации AlexNet. При чтении данных мы сделали дополнительный шаг, чтобы увеличить высоту и ширину изображения до 224 высоты и ширины изображения, используемых AlexNet. Это можно сделать с помощьюtorchvision.transforms.Resize
пример для достижения. То есть мыToTensor
использовать перед экземпляромResize
экземпляр, затем используйтеCompose
instance для объединения двух преобразований для упрощения вызова.
def load_data_fashion_mnist(batch_size, resize=None, root='~/Datasets/FashionMNIST'):
"""Use torchvision.datasets module to download the fashion mnist dataset and then load into memory."""
trans = []
if resize:
trans.append(torchvision.transforms.Resize(size=resize))
trans.append(torchvision.transforms.ToTensor())
transform = torchvision.transforms.Compose(trans)
mnist_train = torchvision.datasets.FashionMNIST(root=root, train=True, download=True, transform=transform)
mnist_test = torchvision.datasets.FashionMNIST(root=root, train=False, download=True, transform=transform)
if sys.platform.startswith('win'):
num_workers = 0
else:
num_workers = 4
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
return train_iter, test_iter
load_data_fashion_mnist()
Метод определяет, как читать данные, Fashion-MNIST изначально был размером 1×28×28.resize
Размер изображения изменяется на основе исходного изображения, и изображение можно настроить на нужный нам размер.
def train(net, train_iter, test_iter, batch_size, optimizer, num_epochs, device=mlutils.try_gpu()):
net = net.to(device)
print("training on", device)
loss = torch.nn.CrossEntropyLoss()
timer = mlutils.Timer()
# in one epoch, it will iterate all training samples
for epoch in range(num_epochs):
# Accumulator has 3 parameters: (loss, train_acc, number_of_images_processed)
metric = mlutils.Accumulator(3)
# all training samples will be splited into batch_size
for X, y in train_iter:
timer.start()
# set the network in training mode
net.train()
# move data to device (gpu)
X = X.to(device)
y = y.to(device)
y_hat = net(X)
l = loss(y_hat, y)
optimizer.zero_grad()
l.backward()
optimizer.step()
with torch.no_grad():
# all the following metrics will be accumulated into variable `metric`
metric.add(l * X.shape[0], mlutils.accuracy(y_hat, y), X.shape[0])
timer.stop()
# metric[0] = l * X.shape[0], metric[2] = X.shape[0]
train_l = metric[0]/metric[2]
# metric[1] = number of correct predictions, metric[2] = X.shape[0]
train_acc = metric[1]/metric[2]
test_acc = mlutils.evaluate_accuracy_gpu(net, test_iter)
if epoch % 1 == 0:
print(f'epoch {epoch + 1} : loss {train_l:.3f}, train acc {train_acc:.3f}, test acc {test_acc:.3f}')
# after training, calculate images/sec
# variable `metric` is defined in for loop, but in Python it can be referenced after for loop
print(f'total training time {timer.sum():.2f}, {metric[2] * num_epochs / timer.sum():.1f} images/sec ' f'on {str(device)}')
на протяжении всей программыmain()
метод, сначала определите сеть, а затем используйтеload_data_fashion_mnist()
Загрузите обучающие и тестовые данные и, наконец, используйтеtrain()
Метод обучения модели:
def main(args):
net = AlexNet()
optimizer = torch.optim.Adam(net.parameters(), lr=args.lr)
# load data
train_iter, test_iter = mlutils.load_data_fashion_mnist(batch_size=args.batch_size, resize=224)
# train
train(net, train_iter, test_iter, args.batch_size, optimizer, args.num_epochs)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Image classification')
parser.add_argument('--batch_size', type=int, default=128, help='batch size')
parser.add_argument('--num_epochs', type=int, default=10, help='number of train epochs')
parser.add_argument('--lr', type=float, default=0.001, help='learning rate')
args = parser.parse_args()
main(args)
в,args
это аргумент, который можно передать в командной строке.
Я загрузил исходный код наGitHubи предоставляет две версии PyTorch и TensorFlow.
использованная литература
- Krizhevsky, A., Sutskever, I., & Hinton, G. E. (2012). Imagenet classification with deep convolutional neural networks. In Advances in neural information processing systems (pp. 1097-1105).
- 2 come.love/chapter_con…
- Тан Шусен Что/погружение-в-Д…