Домашняя страница курса:ML 2021 Spring (ntu.edu.tw)
библиотека
# PyTorch
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
# For data preprocess
import numpy as np
import csv
import os
# For plotting
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
myseed = 42069 # set a random seed for reproducibility
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
np.random.seed(myseed)
torch.manual_seed(myseed)
if torch.cuda.is_available():
torch.cuda.manual_seed_all(myseed)
обработка данных
1. Загрузка данных
Kaggle: ML2021Spring-hw1 | Kaggle
2. Просмотр данных
Набор данных разделен на данные обучения и данные тестирования.
Обзорные данные:
3. Предварительная обработка
Три набора данных:
-
train
: Обучающий набор -
dev
: набор проверки -
test
: тестовый набор (без цели)
Предварительная обработка:
- читать CSV-файл
- Извлечение признаков
- будет
covid.train.csv
Делится на тренировочный набор и тестовый набор - нормализованные данные
читать данные
Мы демонстрируем на упрощенном наборе данных.
path = 'DataExample.csv'
with open(path, 'r') as fp:
data = list(csv.reader(fp))
data = np.array(data[1:])[:, 1:].astype(float)
Мы демонстрируем на упрощенном наборе данных.
DataExample
id | AL | AK | AZ | cli | ili | hh_cmnty_cli | tested_positive |
---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0.81461 | 0.7713562 | 25.6489069 | 19.586492 |
1 | 1 | 0 | 0 | 0.8389952 | 0.8077665 | 25.6791006 | 20.1518381 |
2 | 1 | 0 | 0 | 0.8978015 | 0.8878931 | 26.0605436 | 20.7049346 |
3 | 1 | 0 | 0 | 0.9728421 | 0.9654959 | 25.7540871 | 21.2929114 |
4 | 1 | 0 | 0 | 0.9553056 | 0.9630788 | 25.9470152 | 21.1666563 |
Преобразование данных в хранилище списков
data = list(csv.reader(fp))
print(data)
out:
[['id', 'AL', 'AK', 'AZ', 'cli', 'ili', 'hh_cmnty_cli', 'tested_positive'],
['0', '1', '0', '0', '0.81461', '0.7713562', '25.6489069', '19.586492'],
['1', '1', '0', '0', '0.8389952', '0.8077665', '25.6791006', '20.1518381'],
['2', '1', '0', '0', '0.8978015', '0.8878931', '26.0605436', '20.7049346'],
['3', '1', '0', '0', '0.9728421', '0.9654959', '25.7540871', '21.2929114'],
['4', '1', '0', '0', '0.9553056', '0.9630788', '25.9470152', '21.1666563']]
Но нам не нужны строка 1 и столбец 1
data = np.array(data[1:]) # 删去了第一行
print(data)
out:
[['0' '1' '0' '0' '0.81461' '0.7713562' '25.6489069' '19.586492']
['1' '1' '0' '0' '0.8389952' '0.8077665' '25.6791006' '20.1518381']
['2' '1' '0' '0' '0.8978015' '0.8878931' '26.0605436' '20.7049346']
['3' '1' '0' '0' '0.9728421' '0.9654959' '25.7540871' '21.2929114']
['4' '1' '0' '0' '0.9553056' '0.9630788' '25.9470152' '21.1666563']]
data = data[:, 1:].astype(float) # 删去了第一列,并把数据类型修改为浮点型
print(data)
out:
[[ 1. 0. 0. 0.81461 0.7713562 25.6489069
19.586492 ]
[ 1. 0. 0. 0.8389952 0.8077665 25.6791006
20.1518381]
[ 1. 0. 0. 0.8978015 0.8878931 26.0605436
20.7049346]
[ 1. 0. 0. 0.9728421 0.9654959 25.7540871
21.2929114]
[ 1. 0. 0. 0.9553056 0.9630788 25.9470152
21.1666563]]
поднабор данных
DataExample
1 | 0 | 0 | 0.81461 | 0.7713562 | 25.6489069 | 19.586492 | 0.8389952 | 0.8077665 | 25.6791006 | 20.1518381 | 0.8978015 | 0.8878931 | 26.0605436 | 20.7049346 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0.8389952 | 0.8077665 | 25.6791006 | 20.1518381 | 0.8978015 | 0.8878931 | 26.0605436 | 20.7049346 | 0.9728421 | 0.9654959 | 25.7540871 | 21.2929114 |
1 | 0 | 0 | 0.8978015 | 0.8878931 | 26.0605436 | 20.7049346 | 0.9728421 | 0.9654959 | 25.7540871 | 21.2929114 | 0.9553056 | 0.9630788 | 25.9470152 | 21.1666563 |
1 | 0 | 0 | 0.9728421 | 0.9654959 | 25.7540871 | 21.2929114 | 0.9553056 | 0.9630788 | 25.9470152 | 21.1666563 | 0.9475134 | 0.9687637 | 26.3505008 | 19.8966066 |
1 | 0 | 0 | 0.9553056 | 0.9630788 | 25.9470152 | 21.1666563 | 0.9475134 | 0.9687637 | 26.3505008 | 19.8966066 | 0.8838331 | 0.8930201 | 26.4806235 | 20.1784284 |
для обучающих данных
feats = list(range(14)) # 14 = 3 + 4 + 4 + 3
target = data[:, -1]
data = data[:, feats]
print(target)
print(data)
out:
[20.7049346 21.2929114 21.1666563 19.8966066 20.1784284] # targets
[[ 1. 0. 0. 0.81461 0.7713562 25.6489069
19.586492 0.8389952 0.8077665 25.6791006 20.1518381 0.8978015
0.8878931 26.0605436]
[ 1. 0. 0. 0.8389952 0.8077665 25.6791006
20.1518381 0.8978015 0.8878931 26.0605436 20.7049346 0.9728421
0.9654959 25.7540871]
[ 1. 0. 0. 0.8978015 0.8878931 26.0605436
20.7049346 0.9728421 0.9654959 25.7540871 21.2929114 0.9553056
0.9630788 25.9470152]
[ 1. 0. 0. 0.9728421 0.9654959 25.7540871
21.2929114 0.9553056 0.9630788 25.9470152 21.1666563 0.9475134
0.9687637 26.3505008]
[ 1. 0. 0. 0.9553056 0.9630788 25.9470152
21.1666563 0.9475134 0.9687637 26.3505008 19.8966066 0.8838331
0.8930201 26.4806235]]
Теперь у нас есть всего 5 данных, затем мы разделим обучающие данные на обучающий набор и тестовый набор.
# for train set
indices = [i for i in range(len(data)) if i % 3 != 0]
print(indices)
out:
[1, 2, 4]
То есть индексы данных обучающего набора равны 1, 2, 4.
Тогда остальное - данные тестового набора
# for dev set
indices_2 = [i for i in range(len(data)) if i %3 == 0]
print(indices_2)
out:
[0, 3]
Затем мы конвертируем данные и таргетинг прямо сейчас в тензор
data = torch.FloatTensor(data[indices])
target = torch.FloatTensor(target[indices])
print(data)
print(target)
out:
tensor([[ 1.0000, 0.0000, 0.0000, 0.8390, 0.8078, 25.6791, 20.1518, 0.8978,
0.8879, 26.0605, 20.7049, 0.9728, 0.9655, 25.7541],
[ 1.0000, 0.0000, 0.0000, 0.8978, 0.8879, 26.0605, 20.7049, 0.9728,
0.9655, 25.7541, 21.2929, 0.9553, 0.9631, 25.9470],
[ 1.0000, 0.0000, 0.0000, 0.9553, 0.9631, 25.9470, 21.1667, 0.9475,
0.9688, 26.3505, 19.8966, 0.8838, 0.8930, 26.4806]])
tensor([21.2929, 21.1667, 20.1784])
нормализация данных
Видно, что размеры данных разных признаков сильно различаются, и чтобы сбалансировать их влияние на модель, необходимо нормализовать данные. путь таков:
Обычно все данные нормализуются так, чтобы они находились между [-1,1] или [0,1]. Вот пример последнего.
Нормализация линейной функции (масштабирование Min-Max):
Для набора данных минимальное значение равно m, а максимальное значение равно M, тогда для любого из данных X формула нормализации будет следующей:
Примечание: этот метод реализует одинаковое масштабирование исходных данных.
0 означает стандартизацию (стандартизация Z-показателя):
Метод нормализации 0-среднего нормализует исходный набор данных до набора данных со средним значением 0 и дисперсией 1. Формула нормализации выглядит следующим образом:
Примечание: Этот метод нормализации требует, чтобы распределение исходных данных можно было аппроксимировать распределением Гаусса, иначе эффект нормализации станет очень плохим!
Здесь мы используем нормализацию с нулевым средним значением.
data[:, 3:] =( data[:, 3:] - data[:, 3:].mean(dim=0, keepdim=True))\
/ data[:, 3:].std(dim=0, keepdim=True) # std 即标准差
print(data)
out:
tensor([[ 1.0000, 0.0000, 0.0000, -1.0037, -1.0104, -1.1051, -1.0286, -1.0893,
-1.1540, 0.0184, 0.1048, 0.7532, 0.6065, -0.8144],
[ 1.0000, 0.0000, 0.0000, 0.0075, 0.0212, 0.8424, 0.0599, 0.8764,
0.5413, -1.0091, 0.9435, 0.3813, 0.5477, -0.3017],
[ 1.0000, 0.0000, 0.0000, 0.9962, 0.9892, 0.2628, 0.9687, 0.2129,
0.6127, 0.9907, -1.0483, -1.1346, -1.1542, 1.1161]])
В домашнем задании я попробовал оба метода и обнаружил, что нормализация линейной функции сходится значительно медленнее, чем нормализация с нулевым средним, а итоговая точность немного ниже.
загрузить данные
A
DataLoader
loads data from a givenDataset
into batches.
Взгляните на взаимосвязь между DataLoader и DataSet и поймите, что такое пакет.
Примечание: shuffle при тестировании должен быть установлен в false, иначе порядок каждого тренировочного сета будет разным и будут ошибки.
4. Полный код
class COVID19Dataset(Dataset):
''' Dataset for loading and preprocessing the COVID19 dataset '''
def __init__(self,
path,
mode='train',
target_only=False):
self.mode = mode
# Read data into numpy arrays
with open(path, 'r') as fp:
data = list(csv.reader(fp))
data = np.array(data[1:])[:, 1:].astype(float)
if not target_only:
# feats存放的就是各个feature再data中对应的index
feats = list(range(93)) # 93 = 40 states + day 1 (18) + day 2 (18) + day 3 (17)
else:
# TODO: Using 40 states & 2 tested_positive features (indices = 57 & 75)
pass
if mode == 'test':
# Testing data
# data: 893 x 93 (40 states + day 1 (18) + day 2 (18) + day 3 (17))
data = data[:, feats]
self.data = torch.FloatTensor(data)
else:
# Training data (train/dev sets)
# data: 2700 x 94 (40 states + day 1 (18) + day 2 (18) + day 3 (18))
target = data[:, -1]
data = data[:, feats]
# Splitting training data into train & dev sets
if mode == 'train':
indices = [i for i in range(len(data)) if i % 10 != 0]
elif mode == 'dev':
indices = [i for i in range(len(data)) if i % 10 == 0]
# Convert data into PyTorch tensors
self.data = torch.FloatTensor(data[indices])
self.target = torch.FloatTensor(target[indices])
# Normalize features (you may remove this part to see what will happen)
self.data[:, 40:] =
(self.data[:, 40:] - self.data[:, 40:].mean(dim=0, keepdim=True))
/ self.data[:, 40:].std(dim=0, keepdim=True)
self.dim = self.data.shape[1]
print('Finished reading the {} set of COVID19 Dataset ({} samples found, each dim = {})'
.format(mode, len(self.data), self.dim))
def __getitem__(self, index):
# Returns one sample at a time
if self.mode in ['train', 'dev']:
# For training
return self.data[index], self.target[index]
else:
# For testing (no target)
return self.data[index]
def __len__(self):
# Returns the size of the dataset
return len(self.data)
# DataLoader
def prep_dataloader(path, mode, batch_size, n_jobs=0, target_only=False):
''' Generates a dataset, then is put into a dataloader. '''
dataset = COVID19Dataset(path, mode=mode, target_only=target_only) # Construct dataset
dataloader = DataLoader(
dataset, batch_size,
shuffle=(mode == 'train'), drop_last=False,
num_workers=n_jobs, pin_memory=True) # Construct dataloader
return dataloader
построение сети
NeuralNet
is annn.Module
designed for regression. The DNN consists of 2 fully-connected layers with ReLU activation. This module also included a functioncal_loss
for calculating loss.
1. ReLU
Нелинейная функция активации
Если функция возбуждения не используется, то в этом случае выход каждого слоя является линейной функцией входа верхнего слоя, тогда сколько бы слоев не было у нейронной сети, выход представляет собой линейную комбинацию входа, что эквивалентно отсутствию скрытого слоя. Это самый примитивный персептрон.
Поэтому мы решили ввести в качестве функции возбуждения нелинейную функцию, чтобы глубокая нейронная сеть имела смысл. Выход больше не является линейной комбинацией входа, но может аппроксимировать любую функцию Самая ранняя идея - использовать сигмовидную функцию или функцию тангенса, выход ограничен, и его легко использовать в качестве входа следующего слоя. .
Why ReLU?
-
При использовании таких функций, как сигмоид, при вычислении функции активации (экспоненциальная операция) объем вычислений велик.При обратном распространении для нахождения градиента ошибки вывод включает деление, и объем вычислений довольно велик. Использование функции активации Relu экономит много вычислений во всем процессе.
-
Для глубоких сетей, когда сигмоидальная функция распространяется обратно, градиент легко исчезает (когда сигмоидальная функция близка к области насыщения, изменение происходит слишком медленно, а производная стремится к 0, что вызовет появление информации). потерь), так что глубокий слой не может быть завершен обучением сети.
-
Relu сделает вывод некоторых нейронов равным 0, что приведет к разреженности сети, уменьшит взаимозависимость параметров и облегчит проблему переобучения.
2. nn.MSEloss()
MSEloss() в pytorch имеет два логических параметра:
параметр | эффект |
---|---|
size_average | усреднять ли после суммирования |
reduce | является ли выход скаляром |
reduction | является ли выход скаляром |
Например!
input = torch.randn(2,3,requires_grad=True) # prediction
target = torch.ones(2,3) # ground truth
print(f'input: {input}\n target: {target})
Out:
input: tensor([[-0.0733, -2.2085, -0.6919],
[-1.1417, -1.1327, -1.5466]], requires_grad=True)
target: tensor([[1., 1., 1.],
[1., 1., 1.]])
default
Значение по умолчанию size_average=True, уменьшить=True. Наконец возвращает скаляр.
loss_1 = nn.MSELoss()
output_1 = loss_1(input,target)
print(f'loss_1: {output_1}')
Out:
loss_1: 1.8783622980117798
size_average=False
То есть сумма не будет делиться на n!
loss_1 = nn.MSELoss(size_average=False)
output_1 = loss_1(input,target)
print(f'loss_1: {output_1}')
Out:
loss_1: 11.819371223449707
reduce=False
Возвратный тензор.
loss_1 = nn.MSELoss(reduce=False)
output_1 = loss_1(input,target)
print(f'loss_1: {output_1}')
Out:
loss_1: tensor([[0.0039, 0.2338, 3.5550],
[0.1358, 2.1851, 0.1533]], grad_fn=<MseLossBackward0>)
Что касается уменьшения, то на самом деле это комбинация size_average и сокращения!
Это часть функции legacy_get_string:
if size_average is None:
size_average = True
if reduce is None:
reduce = True
if size_average and reduce:
ret = 'mean'
elif reduce:
ret = 'sum'
else:
ret = 'none'
if emit_warning:
warnings.warn(warning.format(ret))
return ret
3. Regularization
Исходная линейная модель:
Это:
Но как определить n? Или как узнать, сколько функций установить для x? Если размер x слишком высок, его легко переобучить, а если он слишком мал, он будет неподходящим. В этот раз, чтобы ослабить влияние некоторых измерений и сделать функцию гладкой, мы можем упорядочить функцию.
Здесь следует отметить, что переоснащение и недообучение происходят, когдатестовый наборначальство. Для тренировочного набора чем выше размерность, тем лучше эффект подгонки, как показано на рисунке:
Чем выше размерность, тем больше функциональная область, поэтому, конечно, она может охватыватьОбучающий наборлучшая функция на .
4. Полный код
class NeuralNet(nn.Module):
''' A simple fully-connected deep neural network '''
def __init__(self, input_dim):
super(NeuralNet, self).__init__()
# Define your neural network here
# TODO: How to modify this model to achieve better performance?
self.net = nn.Sequential(
nn.Linear(input_dim, 64),
nn.ReLU(),
nn.Linear(64, 1)
)
# Mean squared error loss
self.criterion = nn.MSELoss(reduction='mean')
def forward(self, x):
''' Given input of size (batch_size x input_dim), compute output of the network '''
return self.net(x).squeeze(1)
def cal_loss(self, pred, target):
''' Calculate loss '''
# TODO: you may implement L1/L2 regularization here
return self.criterion(pred, target)
тренироваться
1. Основные функции
getattr()
Это эквивалентно операции «.». Параметры следующие:
- объект: экземпляр объекта
- имя: строка, имя функции-члена объекта или переменной-члена
- по умолчанию: значение по умолчанию, возвращаемое, когда свойство не существует в объекте
- Исключение: «AttrbuteError» выдается, когда этот атрибут отсутствует и нет возвращаемого значения по умолчанию.
getattr(object, name) = object.name
model.train() v.s. model.eval()
model.train
Включите пакетную нормализацию и исключение.
Если в модели есть слои BN (пакетная нормализация) и Dropout, вам нужно добавить model.train() во время обучения. model.train() должен гарантировать, что слой BN может использовать среднее значение и дисперсию каждого пакета данных. Для Dropout model.train() случайным образом выбирает часть сетевых подключений для обучения и обновления параметров.
model.eval()
Пакетная нормализация и отсев не включены.
Если в модели есть слои BN (пакетная нормализация) и Dropout, при тестировании добавьте model.eval(). model.eval() должен гарантировать, что слой BN может использовать среднее значение и дисперсию всех обучающих данных, то есть гарантировать, что среднее значение и дисперсия слоя BN останутся неизменными в процессе тестирования. Для Dropout model.eval() использует все сетевые подключения, то есть не отбрасывает нейроны случайным образом.
После обучения образцов поезда сгенерированная модель модели используется для тестирования образцов. Перед моделью(тестом) нужно добавить model.eval(), иначе при наличии входных данных она изменит вес, даже если не обучена. Это свойство привносится в модель слоем BN и Dropout.
detach().cpu()
detach()
- Функция: Блокировать обратное распространение
- Возвращаемое значение: тензор, но переменная все еще находится на GPU
cpu()
- Роль: переместить данные в ЦП
- Возвращаемое значение: тензор
item()
- Функция: Получить значение тензора (в тензоре может быть только один элемент!)
numpy()
- Функция: преобразовать тензор в массив numpy.
2. Основная рутина
В процессе обхода эпох во время обучения мы часто используемoptimizer.zero_grad(),loss.backward()иoptimizer.step()три функции.
Например:
while epoch < n_epochs:
model.train() # set model to training mode
for x, y in tr_set: # iterate through the dataloader
optimizer.zero_grad() # set gradient to zero
x, y = x.to(device), y.to(device) # move data to device (cpu/cuda)
pred = model(x) # forward pass (compute output)
mse_loss = model.cal_loss(pred, y) # compute loss
mse_loss.backward() # compute gradient (backpropagation)
optimizer.step() # update model with optimizer
loss_record['train'].append(mse_loss.detach().cpu().item())
....
В целом они работают следующим образом:
-
optimizer.zero_grad(): нулевой градиент
- В процессе обучения обычно используется метод мини-пакетов, поэтому, если градиент не очищен, градиент будет связан с данными предыдущего пакета, поэтому эту функцию следует писать перед обратным распространением и градиентным спуском.
-
loss.backward(): обратное распространение вычисляет градиент для каждого параметра
- Если tensor.backward() не выполняется, значение градиента будет равно None, поэтому loss.backward() следует писать перед оптимизатором.step().
-
optimizer.step(): параметры обновления градиентного спуска
- Роль функции step() заключается в выполнении шага оптимизации и обновлении значения параметров с помощью метода градиентного спуска. Поскольку градиентный спуск основан на градиентах, функция loss.backward() должна выполняться перед функцией optimizer.step() для вычисления градиента.
Переменные общих параметров в функциях:
-
param_groups: класс Optimizer создаст список param_groups в конструкторе при создании его экземпляра.В списке имеется num_groups словарей param_group длины 6 (количество_групп зависит от того, сколько наборов параметров было передано при определении оптимизатора), и каждый param_group содержит [' params', 'lr', 'импульс', 'демпфирование', 'weight_decay', 'nesterov'] Эти 6 наборов пар ключ-значение.
-
param_group['params']: Список, состоящий из входящих параметров модели, то есть параметров группы, переданных при создании экземпляра класса Optimizer. Если параметры не сгруппированы, они являются параметрами всей модели model.parameters(), и каждый параметр является объектом torch.nn.parameter.Parameter.
3. Полный код
def train(tr_set, dv_set, model, config, device):
''' DNN training '''
n_epochs = config['n_epochs'] # Maximum number of epochs
# Setup optimizer
optimizer = getattr(torch.optim, config['optimizer'])(
model.parameters(), **config['optim_hparas'])
min_mse = 1000.
loss_record = {'train': [], 'dev': []} # for recording training loss
early_stop_cnt = 0
epoch = 0
while epoch < n_epochs:
model.train() # set model to training mode
for x, y in tr_set: # iterate through the dataloader
optimizer.zero_grad() # set gradient to zero
x, y = x.to(device), y.to(device) # move data to device (cpu/cuda)
pred = model(x) # forward pass (compute output)
mse_loss = model.cal_loss(pred, y) # compute loss
mse_loss.backward() # compute gradient (backpropagation)
optimizer.step() # update model with optimizer
loss_record['train'].append(mse_loss.detach().cpu().item())
# After each epoch, test your model on the validation (development) set.
dev_mse = dev(dv_set, model, device)
if dev_mse < min_mse:
# Save model if your model improved
min_mse = dev_mse
print('Saving model (epoch = {:4d}, loss = {:.4f})'
.format(epoch + 1, min_mse))
torch.save(model.state_dict(), config['save_path']) # Save model to specified path
early_stop_cnt = 0
else:
early_stop_cnt += 1
epoch += 1
loss_record['dev'].append(dev_mse)
if early_stop_cnt > config['early_stop']:
# Stop training if your model stops improving for "config['early_stop']" epochs.
break
print('Finished training after {} epochs'.format(epoch))
return min_mse, loss_record
проверять
dev() очень похож на train(), но обратите внимание, что модель работает в режиме eval(), то есть без BN и Dropout.
полный код
def dev(dv_set, model, device):
model.eval() # set model to evalutation mode
total_loss = 0
for x, y in dv_set: # iterate through the dataloader
x, y = x.to(device), y.to(device) # move data to device (cpu/cuda)
with torch.no_grad(): # disable gradient calculation
pred = model(x) # forward pass (compute output)
mse_loss = model.cal_loss(pred, y) # compute loss
total_loss += mse_loss.detach().cpu().item() * len(x) # accumulate loss
total_loss = total_loss / len(dv_set.dataset) # compute averaged loss
return total_loss
контрольная работа
1. torch.cat()
cat() может объединять несколько тензоров вместе.
параметр:
- входы: последовательность соединяемых тензоров, которая может быть любой последовательностью того же типа тензора.
- dim: выбранный размер расширения, должен быть в
0
прибытьlen(inputs[0])
между, соединить последовательности тензоров по этому измерению
Уведомление
- Входные данные должны быть последовательностью, а данные в последовательности — любым тензором того же типа с той же формой.
- Размерность не может превышать размерность любого тензора входных данных
Например:
t1 = torch.Tensor([1,2,3])
t2 = torch.Tensor([4,5,6])
t3 = torch.Tensor([7,8,9])
list = [t1,t2,t3]
t = torch.cat(list,dim=0)
print(t)
Out:
tensor([1., 2., 3., 4., 5., 6., 7., 8., 9.])
Изменить яркость:
t1 = torch.Tensor([[1,2,3],[3,2,1]])
t2 = torch.Tensor([[4,5,6],[6,5,4]])
t3 = torch.Tensor([[7,8,9],[9,8,7]])
list = [t1,t2,t3]
t = torch.cat(list,dim=1)
print(t)
Out:
tensor([[1., 2., 3., 4., 5., 6., 7., 8., 9.],
[3., 2., 1., 6., 5., 4., 9., 8., 7.]])
2. Полный код
def test(tt_set, model, device):
model.eval() # set model to evalutation mode
preds = []
for x in tt_set: # iterate through the dataloader
x = x.to(device) # move data to device (cpu/cuda)
with torch.no_grad(): # disable gradient calculation
pred = model(x) # forward pass (compute output)
preds.append(pred.detach().cpu()) # collect prediction
preds = torch.cat(preds, dim=0).numpy() # concatenate all predictions and convert to a numpy array
return preds
Установить гиперпараметры
1. Полный код
device = get_device() # get the current available device ('cpu' or 'cuda')
os.makedirs('models', exist_ok=True) # The trained model will be saved to ./models/
target_only = False # TODO: Using 40 states & 2 tested_positive features
# TODO: How to tune these hyper-parameters to improve your model's performance?
config = {
'n_epochs': 3000, # maximum number of epochs
'batch_size': 270, # mini-batch size for dataloader
'optimizer': 'SGD', # optimization algorithm (optimizer in torch.optim)
'optim_hparas': { # hyper-parameters for the optimizer (depends on which optimizer you are using)
'lr': 0.001, # learning rate of SGD
'momentum': 0.9 # momentum for SGD
},
'early_stop': 200, # early stopping epochs (the number epochs since your model's last improvement)
'save_path': 'models/model.pth' # your model will be saved here
}
Загрузить данные и модели
1. Полный код
tr_set = prep_dataloader(tr_path, 'train', config['batch_size'], target_only=target_only)
dv_set = prep_dataloader(tr_path, 'dev', config['batch_size'], target_only=target_only)
tt_set = prep_dataloader(tt_path, 'test', config['batch_size'], target_only=target_only)
Начинать!
model_loss, model_loss_record = train(tr_set, dv_set, model, config, device)
визуализация данных
1. training
Давайте сначала посмотрим на изменение MSEloss по мере увеличения количества тренировок.
plot_learning_curve(model_loss_record, title='deep model')
Посмотрите еще раз на эффект предсказания.
del model
model = NeuralNet(tr_set.dataset.dim).to(device)
ckpt = torch.load(config['save_path'], map_location='cpu') # Load your best model
model.load_state_dict(ckpt)
plot_pred(dv_set, model, device) # Show prediction on the validation set
Точки на синей линии показывают, что прогнозируемое значение равно фактическому значению.
2. testing
def save_pred(preds, file):
''' Save predictions to specified file '''
print('Saving results to {}'.format(file))
with open(file, 'w') as fp:
writer = csv.writer(fp)
writer.writerow(['id', 'tested_positive'])
for i, p in enumerate(preds):
writer.writerow([i, p])
preds = test(tt_set, model, device) # predict COVID-19 cases with your model
save_pred(preds, 'pred.csv') # save prediction file to pred.csv
пред результат:
Improvements
Нам нужно изменить пример кода, чтобы сделать модель лучше!
Public leaderboard
- simple baseline: 2.04826
- medium baseline: 1.36937
- strong baseline: 0.89266
Hints
- Feature selection (what other features are useful?)
- DNN architecture (layers? dimension? activation function?)
- Training (mini-batch? optimizer? learning rate?)
- L2 regularization
- There are some mistakes in the sample code, can you find them?
TODO1: изменить функции, используемые для обучения
существуетCOVID19Dataset
, мы можем изменить извлеченные функции.
if not target_only:
# feats存放的就是各个feature再data中对应的index
feats = list(range(93)) # 93 = 40 states + day 1 (18) + day 2 (18) + day 3 (17)
else:
# TODO: Using 40 states & 2 tested_positive features (indices = 57 & 75)
# 仅使用42个特征
feats = list(range(40))
feats.append(57)
feats.append(75)
pass
Приходите посмотреть на окончательный результат!
Скорость сходимости значительно улучшена!
Счет:
TODO2: добавить регуляризацию
При настройке гиперпараметров добавьте регуляризацию.
config = {
'n_epochs': 3000, # maximum number of epochs
'batch_size': 270, # mini-batch size for dataloader
'optimizer': 'SGD', # optimization algorithm (optimizer in torch.optim)
'optim_hparas': { # hyper-parameters for the optimizer (depends on which optimizer you are using)
'lr': 0.001, # learning rate of SGD
'momentum': 0.9 # momentum for SGD
'weight_decay': 0.1 # regularization
},
Результаты улучшились, но незначительно, а скорость сходимости замедлилась (разумно)...
TODO3: изменить структуру нейронной сети
Add more layers
class NeuralNet(nn.Module):
''' A simple fully-connected deep neural network '''
def __init__(self, input_dim):
super(NeuralNet, self).__init__()
# Define your neural network here
# TODO: How to modify this model to achieve better performance?
self.net = nn.Sequential(
nn.Linear(input_dim, 128),
nn.ReLU(),
nn.Linear(128,64),
nn.ReLU(),
nn.Linear(64, 1)
)
...
Настройка параметров по желанию, просто добавление линейного слоя и слоя ReLU...
Определенный эффект есть, но не очень существенный...
PReLU
Измените слой ReLU на PReLU, эффект растягивается...
TODO4: Оптимизатор
Adam
Трудно сказать, чуть хуже SGD.
И MSEloss, кажется, немного колеблется...
TODO5: исправить ошибки
Для стандартизации тестовой выборки следует выбирать среднее значение и дисперсию обучающей выборки, поскольку тестовая выборка может быть небольшой, а среднее значение и дисперсия не могут отражать характеристики большого количества данных.
if mode == 'test':
# Testing data
# data: 893 x 93 (40 states + day 1 (18) + day 2 (18) + day 3 (17))
data = data[:, feats]
self.data = torch.FloatTensor(data)
self.data[:, 40:] = \
(self.data[:, 40:] - self.data[:, 40:].mean(dim=0, keepdim=True)) \
/ self.data[:, 40:].std(dim=0, keepdim=True)
else:
# Training data (train/dev sets)
# data: 2700 x 94 (40 states + day 1 (18) + day 2 (18) + day 3 (18))
target = data[:, -1]
data = data[:, feats]
indices_train = [i for i in range(len(data)) if i % 10 != 0]
tr_data = self.data = torch.FloatTensor(data[indices_train])
tr_mean = tr_data[:, 40:].mean(dim=0, keepdim=True)
tr_std = tr_data[:, 40:].std(dim=0, keepdim=True)
# Splitting training data into train & dev sets
if mode == 'train':
indices = indices_train
self.data = tr_data
self.target = torch.FloatTensor(target[indices])
elif mode == 'dev':
indices = [i for i in range(len(data)) if i % 10 == 0]
self.data = torch.FloatTensor(data[indices])
self.target = torch.FloatTensor(target[indices])
self.data[:, 40:] = \
(self.data[:, 40:] - tr_mean) \
/ tr_std
Но результат странный, оценка намного хуже...
впечатление
На настройку параметров ушло два дня, но в итоге эффект был не так хорош, как в первый раз... Чувствую, что недостаточно знаком с моделью и не имею опыта настройки параметров. Это домашнее задание на данный момент подошло к концу. Если у вас будет много знаний в этот день, вы сможете пересечь сильный базовый уровень...
Some useful links
Colab: ML2021Spring - HW1.ipynb - Colaboratory (google.com)
Pytorch Totorial P1: ML2021 Pytorch tutorial part 1 - YouTube
Kaggle: ML2021Spring-hw1 | Kaggle
Примечания к исследованию:Весенний курс машинного обучения Ли Хонги, видеозаметки 1: Введение, Учебники по Colab и PyTorch, HW1 - Требуется программист
Регуляризация:Метод pytorch для достижения регуляризации регуляризации L2 и L1 - Требуется программист
Activation Function: Понимание часто используемой функции активации (функции возбуждения) и блога summary_tyhj_sf — функция CSDN blog_activation
HWExample: 2021 Li Hongyi Machine Learning Course Homework 1 — Short Books (jianshu.com)
Cross baseline: Li Hongyi ML2021Spring HW1 - lizhi334 - Blog Park (cnblogs.com)
Reference
РеЛУ:Роль Relu - Блог KMITA - Блог CSDN
поезд() и eval():Pytorch: использование и разница между model.train() и model.eval(), а также разница между model.eval() и torch.no_grad() Develop Paper
оптимизатор.шаг():Понимание функции и принципа работы optimizer.zero_grad(), loss.backward(), optimizer.step() - Требуется программист
Source: Heng-Jui Chang @ NTUEE (GitHub.com/karma642381/ml…)