ML 2021 Spring HW1: Regression

машинное обучение

            ML 2021 Spring HW1: Regression

Домашняя страница курса: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. Просмотр данных

Набор данных разделен на данные обучения и данные тестирования.

Обзорные данные:

image.png

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 формула нормализации будет следующей:

Xnorm=XmMmX_{norm} = \frac{X - m}{M- m}

Примечание: этот метод реализует одинаковое масштабирование исходных данных.

0 означает стандартизацию (стандартизация Z-показателя):

Метод нормализации 0-среднего нормализует исходный набор данных до набора данных со средним значением 0 и дисперсией 1. Формула нормализации выглядит следующим образом:

z=xμоz = \frac{x-\mu}{\sigma}

Примечание: Этот метод нормализации требует, чтобы распределение исходных данных можно было аппроксимировать распределением Гаусса, иначе эффект нормализации станет очень плохим!

Здесь мы используем нормализацию с нулевым средним значением.

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 given Dataset into batches.

Взгляните на взаимосвязь между DataLoader и DataSet и поймите, что такое пакет.

image.png

Примечание: 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 an nn.Module designed for regression. The DNN consists of 2 fully-connected layers with ReLU activation. This module also included a function cal_loss for calculating loss.

1. ReLU

这里写图片描述

Нелинейная функция активации

Если функция возбуждения не используется, то в этом случае выход каждого слоя является линейной функцией входа верхнего слоя, тогда сколько бы слоев не было у нейронной сети, выход представляет собой линейную комбинацию входа, что эквивалентно отсутствию скрытого слоя. Это самый примитивный персептрон.

Поэтому мы решили ввести в качестве функции возбуждения нелинейную функцию, чтобы глубокая нейронная сеть имела смысл. Выход больше не является линейной комбинацией входа, но может аппроксимировать любую функцию Самая ранняя идея - использовать сигмовидную функцию или функцию тангенса, выход ограничен, и его легко использовать в качестве входа следующего слоя. .

Why ReLU?

  • При использовании таких функций, как сигмоид, при вычислении функции активации (экспоненциальная операция) объем вычислений велик.При обратном распространении для нахождения градиента ошибки вывод включает деление, и объем вычислений довольно велик. Использование функции активации Relu экономит много вычислений во всем процессе.

  • Для глубоких сетей, когда сигмоидальная функция распространяется обратно, градиент легко исчезает (когда сигмоидальная функция близка к области насыщения, изменение происходит слишком медленно, а производная стремится к 0, что вызовет появление информации). потерь), так что глубокий слой не может быть завершен обучением сети.

  • Relu сделает вывод некоторых нейронов равным 0, что приведет к разреженности сети, уменьшит взаимозависимость параметров и облегчит проблему переобучения.

2. nn.MSEloss()

loss(y^,y)=1n(y^iyi)2loss({\hat{y},y})=\frac1 n \displaystyle\sum(\hat{y}_i-y_i)^2

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

Исходная линейная модель:

f(x)=b+wTxf(\mathbf{x}) =b+\mathbf{w}^\mathrm{T}\mathbf{x}\\

Это:

f(x)=b+w1x1+w2x2+...+wnxnf(\mathbf{x})=b+w_1x_1+w_2x_2+...+w_nx_n

Но как определить n? Или как узнать, сколько функций установить для x? Если размер x слишком высок, его легко переобучить, а если он слишком мал, он будет неподходящим. В этот раз, чтобы ослабить влияние некоторых измерений и сделать функцию гладкой, мы можем упорядочить функцию.

L(w,,b)=(yi(b+wTxi))2+λi(wi)2L(\mathbf{w,},b)=\displaystyle\sum(y_i-(b+\mathbf{w}^\mathrm{T}\mathbf{x_i}))^2+\lambda\displaystyle\sum_i (w_i)^2

Здесь следует отметить, что переоснащение и недообучение происходят, когдатестовый наборначальство. Для тренировочного набора чем выше размерность, тем лучше эффект подгонки, как показано на рисунке:

image.png

Чем выше размерность, тем больше функциональная область, поэтому, конечно, она может охватыватьОбучающий наборлучшая функция на .

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')

image.png

Посмотрите еще раз на эффект предсказания.

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

image.png

Точки на синей линии показывают, что прогнозируемое значение равно фактическому значению.

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

пред результат:

image.png

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

Приходите посмотреть на окончательный результат!

image.png

Скорость сходимости значительно улучшена!

image.png

image.png

Счет:

image.png

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
    },

Результаты улучшились, но незначительно, а скорость сходимости замедлилась (разумно)...

image.png

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...

Определенный эффект есть, но не очень существенный...

image.png

PReLU

Измените слой ReLU на PReLU, эффект растягивается...

image.png

TODO4: Оптимизатор

Adam

Трудно сказать, чуть хуже SGD.

И MSEloss, кажется, немного колеблется...

image.png

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

Но результат странный, оценка намного хуже...

image.png

впечатление

На настройку параметров ушло два дня, но в итоге эффект был не так хорош, как в первый раз... Чувствую, что недостаточно знаком с моделью и не имею опыта настройки параметров. Это домашнее задание на данный момент подошло к концу. Если у вас будет много знаний в этот день, вы сможете пересечь сильный базовый уровень...

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…)