Implementação em Python da série de aprendizado profundo [propagação direta e propagação reversa]

Prefácio

Antes de entender a estrutura de aprendizado profundo, precisamos entender ou mesmo implementar um processo de aprendizado em rede e ajuste de parâmetros por nós mesmos, e então entender o mecanismo de aprendizado profundo;

Por esse motivo, o blogueiro aqui fornece um exemplo escrito por ele mesmo para levar todos a entender o processo de propagação para a frente e para trás da aprendizagem online;

Além disso, para obter a leitura em lote, também projetei e forneci uma classe DataLoader simples para simular a amostragem de iteradores de dados no aprendizado profundo e forneci uma função para acessar o modelo;


Vale ressaltar que ele é implementado em python , portanto a demanda pelo ambiente não é muito grande. Espero que você possa estrelar meu blog e github mais para aprender mais conhecimentos úteis! !


índice

Um, alcance o efeito

Em segundo lugar, a estrutura geral do código

Três, descrição detalhada do código

1. Processamento de dados

2. Projeto de rede

3. Função de ativação

4. Treinamento

Quatro, demonstração de treinamento

Cinco, resumo

 


Um, alcance o efeito

Implemente uma rede composta de várias camadas lineares para se adequar à função, endereço do projeto: https://github.com/nickhuang1996/HJLNet , execute:

python demo.py

A função de ajuste é :y = \ sin (2 \ pi x), 0 \ leqslant x \ leqslant 2

Os seguintes resultados são da esquerda para a direita (a taxa de aprendizagem é 0,03, o tamanho do lote é 90):

Época: 400, 1000, 2000, 10000 ou mais


Em segundo lugar, a estrutura geral do código


Três, descrição detalhada do código

1. Processamento de dados

Dataset.py

x são os dados entre 0 e 2 e o tamanho do passo é 0,01, portanto são 200 dados;

y é a função objetivo, a amplitude é 20;

comprimento é o comprimento dos dados;

_build_items () é construir um dicionário para armazenar x e y;

_transform () é transformar os dados xey;

import numpy as np


class Dataset:
    def __init__(self):

        self.x = np.arange(0.0, 2.0, 0.01)
        self.y = 20 * np.sin(2 * np.pi * self.x)
        self.length = len(list(self.x))
        self._build_items()
        self._transform()

    def _build_items(self):
        self.items = [{
            'x': list(self.x)[i],
            'y': list(self.y)[i]
        }for i in range(self.length)]

    def _transform(self):
        self.x = self.x.reshape(1, self.__len__())
        self.y = self.y.reshape(1, self.__len__())

    def __len__(self):
        return self.length

    def __getitem__(self, index):
        return self.items[index]

DataLoader.py

Semelhante ao DataLoader no Pytorch, o blogger também passa dois parâmetros para inicialização aqui: dataset e batch_size

__next __ () é a função executada em cada iteração, usando __len __ () para obter o comprimento do conjunto de dados e usando __getitem __ () para obter os dados no conjunto de dados;

_concate () é concatenar os dados de um lote;

_transform () é transformar a forma de dados de um lote;

import numpy as np


class DataLoader:
    def __init__(self, dataset, batch_size):
        self.dataset = dataset
        self.batch_size = batch_size
        self.current = 0

    def __next__(self):
        if self.current < self.dataset.__len__():
            if self.current + self.batch_size <= self.dataset.__len__():
                item = self._concate([self.dataset.__getitem__(index) for index in range(self.current, self.current + self.batch_size)])
                self.current += self.batch_size
            else:
                item = self._concate([self.dataset.__getitem__(index) for index in range(self.current, self.dataset.__len__())])
                self.current = self.dataset.__len__()
            return item
        else:
            self.current = 0
            raise StopIteration

    def _concate(self, dataset_items):
        concated_item = {}
        for item in dataset_items:
            for k, v in item.items():
                if k not in concated_item:
                    concated_item[k] = [v]
                else:
                    concated_item[k].append(v)
        concated_item = self._transform(concated_item)
        return concated_item

    def _transform(self, concated_item):
        for k, v in concated_item.items():
            concated_item[k] = np.array(v).reshape(1, len(v))
        return concated_item

    def __iter__(self):
        return self

2. Projeto de rede

Linear.py

Semelhante ao Linear no Pytorch, o blogger também passa três parâmetros para inicialização aqui: in_features, out_features, bias

_init_parameters () é o peso de inicialização peso e polarização de polarização , o peso tamanho é [out_features, in_features] , e o polarização tamanho é [out_features, 1]

forward é propagação direta:y = wx + b

import numpy as np


class Linear:
    def __init__(self, in_features, out_features, bias=False):
        self.in_features = in_features
        self.out_features = out_features
        self.bias = bias
        self._init_parameters()

    def _init_parameters(self):
        self.weight = np.random.random([self.out_features, self.in_features])
        if self.bias:
            self.bias = np.zeros([self.out_features, 1])
        else:
            self.bias = None

    def forward(self, input):
        return self.weight.dot(input) + self.bias

* network.py

Uma rede linear simples de várias camadas

_init_parameters () é armazenar o peso e a paranóia na camada Linear em um dicionário;

forward () é propagação direta e a última camada não passa por Sigmoid;

backward () é a propagação reversa, usando gradiente descendente para obter transferência de erro e ajuste de parâmetro: por exemplo, a propagação reversa de uma camada Linear de duas camadas é a seguinte

dz <[1]} = a <[1]} - y}

dW ^ {[1]} = dz ^ {[1]} a ^ {[1]} ^ {T}}

db ^ {[1]} = dz ^ {[1]}

dz ^ {[0]} = W ^ {[1]} ^ {T} dz ^ {[1]} \ ast S ^ {[0]} '(z ^ {[0]})}

dW ^ {[0]} = dz ^ {[0]} x ^ {T}}

db ^ {[0]} = dz ^ {[0]}

update_grads () é o peso e o viés da atualização;

# -*- coding: UTF-8 -*-
import numpy as np
from ..lib.Activation.Sigmoid import sigmoid_derivative, sigmoid
from ..lib.Module.Linear import Linear

class network:
    def __init__(self, layers_dim):
        self.layers_dim = layers_dim
        self.linear_list = [Linear(layers_dim[i - 1], layers_dim[i], bias=True) for i in range(1, len(layers_dim))]
        self.parameters = {}
        self._init_parameters()

    def _init_parameters(self):
        for i in range(len(self.layers_dim) - 1):
            self.parameters["w" + str(i)] = self.linear_list[i].weight
            self.parameters["b" + str(i)] = self.linear_list[i].bias

    def forward(self, x):
        a = []
        z = []
        caches = {}
        a.append(x)
        z.append(x)

        layers = len(self.parameters) // 2

        for i in range(layers):
            z_temp = self.linear_list[i].forward(a[i])
            self.parameters["w" + str(i)] = self.linear_list[i].weight
            self.parameters["b" + str(i)] = self.linear_list[i].bias
            z.append(z_temp)
            if i == layers - 1:
                a.append(z_temp)
            else:
                a.append(sigmoid(z_temp))
        caches["z"] = z
        caches["a"] = a
        return caches, a[layers]

    def backward(self, caches, output, y):
        layers = len(self.parameters) // 2
        grads = {}
        m = y.shape[1]

        for i in reversed(range(layers)):
            # 假设最后一层不经历激活函数
            # 就是按照上面的图片中的公式写的
            if i == layers - 1:
                grads["dz" + str(i)] = output - y
            else:  # 前面全部都是sigmoid激活
                grads["dz" + str(i)] = self.parameters["w" + str(i + 1)].T.dot(
                    grads["dz" + str(i + 1)]) * sigmoid_derivative(
                    caches["z"][i + 1])
            grads["dw" + str(i)] = grads["dz" + str(i)].dot(caches["a"][i].T) / m
            grads["db" + str(i)] = np.sum(grads["dz" + str(i)], axis=1, keepdims=True) / m
        return grads

    # 就是把其所有的权重以及偏执都更新一下
    def update_grads(self, grads, learning_rate):
        layers = len(self.parameters) // 2
        for i in range(layers):
            self.parameters["w" + str(i)] -= learning_rate * grads["dw" + str(i)]
            self.parameters["b" + str(i)] -= learning_rate * grads["db" + str(i)]

3. Função de ativação

Sigmoid.py

Definição da fórmula:S (x) = \ frac {1} {1 + e ^ {- x}}

A derivada pode ser representada por si mesma:S '(x) = \ frac {e ^ {- x}} {(1 + e ^ {- x}) ^ 2} = S (x) (1-S (x))

import numpy as np


def sigmoid(x):
    return 1.0 / (1.0 + np.exp(-x))


def sigmoid_derivative(x):
    return sigmoid(x) * (1 - sigmoid(x))

4. Treinamento

demo.py

O arquivo de entrada do modelo de treinamento, incluindo modelos de treinamento , teste e armazenamento

from code.scripts.trainer import Trainer
from code.config.default_config import _C


if __name__ == '__main__':
    trainer = Trainer(cfg=_C)
    trainer.train()
    trainer.test()
    trainer.save_models()

default_config.py

Arquivo de configuração :

layers_dim representa as dimensões de entrada e saída da camada Linear;

batch_size é o tamanho do lote;

total_epochs é o tempo geral de treinamento, um treinamento x é uma época;

Currículo é julgar para continuar o treinamento;

result_img_path é o caminho de armazenamento de resultados;

ckpt_path é o caminho onde o modelo está armazenado;

from easydict import EasyDict


_C = EasyDict()
_C.layers_dim = [1, 25, 1] # [1, 30, 10, 1]
_C.batch_size = 90
_C.total_epochs = 40000
_C.resume = True  # False means retraining
_C.result_img_path = "D:/project/Pycharm/HJLNet/result.png"
_C.ckpt_path = 'D:/project/Pycharm/HJLNet/ckpt.npy'

trainer.py

Não vou entrar em detalhes aqui, eu uso principalmente train () para treinamento e test () para teste.

from ..lib.Data.DataLoader import DataLoader
from ..scripts.Dataset import Dataset
from ..scripts.network import network
import matplotlib.pyplot as plt
import numpy as np


class Trainer:
    def __init__(self, cfg):
        self.ckpt_path = cfg.ckpt_path
        self.result_img_path = cfg.result_img_path
        self.layers_dim = cfg.layers_dim
        self.net = network(self.layers_dim)
        if cfg.resume:
            self.load_models()
        self.dataset = Dataset()
        self.dataloader = DataLoader(dataset=self.dataset, batch_size=cfg.batch_size)
        self.total_epochs = cfg.total_epochs
        self.iterations = 0
        self.x = self.dataset.x
        self.y = self.dataset.y
        self.draw_data(self.x, self.y)

    def train(self):
        for i in range(self.total_epochs):

            for item in self.dataloader:
                caches, output = self.net.forward(item['x'])
                grads = self.net.backward(caches, output, item['y'])
                self.net.update_grads(grads, learning_rate=0.03)
                if i % 100 == 0:
                    print("Epoch: {}/{} Iteration: {} Loss: {}".format(i + 1,
                                                                       self.total_epochs,
                                                                       self.iterations,
                                                                       self.compute_loss(output, item['y'])))
                self.iterations += 1

    def test(self):
        caches, output = self.net.forward(self.x)
        self.draw_data(self.x, output)
        self.save_results()
        self.show()

    def save_models(self):
        ckpt = {
            "layers_dim": self.net.layers_dim,
            "parameters": self.net.linear_list
        }
        np.save(self.ckpt_path, ckpt)
        print('Save models finish!!')

    def load_models(self):
        ckpt = np.load(self.ckpt_path).item()
        self.net.layers_dim = ckpt["layers_dim"]
        self.net.linear_list = ckpt["parameters"]
        print('load models finish!!')

    def draw_data(self, x, y):
        plt.scatter(x, y)

    def show(self):
        plt.show()

    def save_results(self):
        plt.savefig(fname=self.result_img_path, figsize=[10, 10])

    # 计算误差值
    def compute_loss(self, output, y):
        return np.mean(np.square(output - y))

Quatro, demonstração de treinamento

Durante o treinamento, o tempo de treinamento, o número de iterações e alterações de perda serão emitidos, e o modelo e os resultados serão armazenados após o treinamento.

1. Comece o treinamento

2. Após o treinamento, leia o último modelo para continuar o treinamento

3. Exibição de resultados


Cinco, resumo

Desta forma, conheço o processo de propagação para frente e para trás de um processo básico de treinamento de rede. Posteriormente, códigos e princípios mais detalhados serão atualizados para ajudá-lo a aprender o conhecimento e os conceitos de aprendizagem profunda

Acho que você gosta

Origin blog.csdn.net/qq_36556893/article/details/109224708
Recomendado
Clasificación