lab
Neural Network
한국어

처음부터 신경망 구축 및 훈련

개요

많은 이론적 지식이 이전에 설명되었으므로 이제 실습을 시작할 때입니다. 신경망을 처음부터 구축하고 전체 프로세스를 함께 묶도록 훈련해 봅시다.


보다 직관적이고 이해하기 쉽도록 다음 원칙을 따릅니다.

  1. 로직을 단순화하기 위해 타사 라이브러리를 사용하지 마십시오.
  2. 성능 최적화 없음: 추가 개념 및 기술 도입을 피하여 복잡성을 증가시킵니다.

데이터세트

먼저 데이터 세트가 필요합니다. 시각화를 용이하게 하기 위해 이진 함수를 목적 함수로 사용하고 샘플링하여 데이터 세트를 생성합니다.


참고: 실제 엔지니어링 프로젝트에서는 목적 함수를 알 수 없지만 샘플링할 수 있습니다.

목적 함수

o(x,y)={1x2+y2<10그렇지 않으면o(x, y) = \begin{cases} 1 & x^2 + y^2 < 1 \\ 0 & \text{그렇지 않으면}\end{cases}

코드 쇼는 아래와 같습니다.

def o(x, y): return 1.0 if x*x + y*y < 1 else 0.0

데이터세트 생성

sample_density = 10 xs = [ [-2.0 + 4 * x/sample_density, -2.0 + 4 * y/sample_density] for x in range(sample_density+1) for y in range(sample_density+1) ] dataset = [ (x, y, o(x, y)) for x, y in xs ]

생성된 데이터 세트는 [[-2.0, -2.0, 0.0], [-2.0, -1.6, 0.0], ...]

데이터세트 이미지

신경망 구축

활성화 기능

import math def sigmoid(x): return 1 / (1 + math.exp(-x))

뉴런

from random import seed, random seed(0) class Neuron: def __init__(self, num_inputs): self.weights = [random()-0.5 for _ in range(num_inputs)] self.bias = 0.0 def forward(self, inputs): # z = wx + b z = sum([ i * w for i, w in zip(inputs, self.weights) ]) + self.bias return sigmoid(z)

뉴런 표현은 다음과 같습니다.

sigmoid(wx+b)\text{sigmoid}(\mathbf w \mathbf x + b)
  • w\mathbf w: 벡터, 코드의 가중치 배열에 해당
  • bb: 코드의 편향에 해당합니다.

참고: 뉴런의 매개변수는 무작위로 초기화됩니다. 그러나 재현 가능한 실험을 위해 임의의 시드가 설정됩니다(seed(0))

신경망

class MyNet: def __init__(self, num_inputs, hidden_shapes): layer_shapes = hidden_shapes + [1] input_shapes = [num_inputs] + hidden_shapes self.layers = [ [ Neuron(pre_layer_size) for _ in range(layer_size) ] for layer_size, pre_layer_size in zip(layer_shapes, input_shapes) ] def forward(self, inputs): for layer in self.layers: inputs = [ neuron.forward(inputs) for neuron in layer ] # 마지막 뉴런의 출력을 반환 return inputs[0]

다음과 같이 신경망을 구성합니다.

net = MyNet(2, [4])

이 시점에서 신경망 기능을 호출할 수 있는 신경망(net)이 있습니다.

print(net.forward([0, 0]))

함수 값 0.55...를 얻으십시오. 이때 신경망은 훈련되지 않은 네트워크입니다.

초기 신경망 기능 이미지

신경망 훈련

손실 함수

먼저 손실 함수를 정의합니다.

def square_loss(predict, target): return (predict-target)**2

그래디언트 계산

특히 심층 신경망의 경우 기울기 계산이 복잡합니다. 역전파 알고리즘은 신경망의 기울기를 계산하도록 특별히 설계된 알고리즘입니다.


복잡하기 때문에 여기서는 설명하지 않습니다. 관심 있는 분들은 다음의 상세한 코드를 참고하시면 됩니다. 또한, 현재 딥러닝 프레임워크는 기울기를 자동으로 계산하는 기능을 가지고 있습니다.


미분 함수를 정의합니다.

def sigmoid_derivative(x): _output = sigmoid(x) return _output * (1 - _output) def square_loss_derivative(predict, target): return 2 * (predict-target)

편미분 찾기(데이터의 일부는 미분을 용이하게 하기 위해 순방향 함수에 캐시됨):

class Neuron: ... def forward(self, inputs): self.inputs_cache = inputs # z = wx + b self.z_cache = sum([ i * w for i, w in zip(inputs, self.weights) ]) + self.bias return sigmoid(self.z_cache) def zero_grad(self): self.d_weights = [0.0 for w in self.weights] self.d_bias = 0.0 def backward(self, d_a): d_loss_z = d_a * sigmoid_derivative(self.z_cache) self.d_bias += d_loss_z for i in range(len(self.inputs_cache)): self.d_weights[i] += d_loss_z * self.inputs_cache[i] return [d_loss_z * w for w in self.weights] class MyNet: ... def zero_grad(self): for layer in self.layers: for neuron in layer: neuron.zero_grad() def backward(self, d_loss): d_as = [d_loss] for layer in reversed(self.layers): da_list = [ neuron.backward(d_a) for neuron, d_a in zip(layer, d_as) ] d_as = [sum(da) for da in zip(*da_list)]
  • 편도함수는 d_weightsd_bias에 각각 저장됩니다.
  • zero_grad 함수는 각 편미분을 포함하여 기울기를 지우는 데 사용됩니다.
  • backward 함수는 편도함수를 계산하고 그 값을 누적 저장하는 데 사용됩니다.

매개변수 업데이트

경사 하강법을 사용하여 매개변수 업데이트:

class Neuron: ... def update_params(self, learning_rate): self.bias -= learning_rate * self.d_bias for i in range(len(self.weights)): self.weights[i] -= learning_rate * self.d_weights[i] class MyNet: ... def update_params(self, learning_rate): for layer in self.layers: for neuron in layer: neuron.update_params(learning_rate)

훈련 수행

def one_step(learning_rate): net.zero_grad() loss = 0.0 num_samples = len(dataset) for x, y, z in dataset: predict = net.forward([x, y]) loss += square_loss(predict, z) net.backward(square_loss_derivative(predict, z) / num_samples) net.update_params(learning_rate) return loss / num_samples def train(epoch, learning_rate): for i in range(epoch): loss = one_step(learning_rate) if i == 0 or (i+1) % 100 == 0: print(f"{i+1} {loss:.4f}")

훈련 2000단계:

train(2000, learning_rate=10)

참고: 여기에서는 프로젝트 상황과 관련된 비교적 큰 학습률이 사용됩니다. 실제 프로젝트의 학습률은 일반적으로 매우 낮습니다

훈련 후 신경망 기능 이미지
log y\text{log y}
손실 곡선

추론

학습 후 모델을 추론에 사용할 수 있습니다.

def inference(x, y): return net.forward([x, y]) print(inference(1, 2))

전체 코드를 참조하세요:nn_from_scratch.py

요약

이 연습의 단계는 다음과 같습니다.

  1. 가상 목적 함수를 구성합니다. o(x,y)o(x, y);
  2. o(x,y)o(x, y)를 샘플링하여 데이터 세트, 즉 데이터 세트 함수 d(x,y)d(x, y)
  3. 은닉층, 즉 신경망 함수 f(x,y)f(x, y)를 사용하여 완전 연결 신경망을 구성했습니다.
  4. 경사하강법을 사용하여 f(x,y)f(x, y)d(x,y)d(x, y)에 근접하도록 신경망을 훈련합니다.

가장 복잡한 부분은 역전파 알고리즘을 사용하는 기울기를 찾는 것입니다. 실제 프로젝트에서 개발을 위해 주류 딥 러닝 프레임워크를 사용하면 그라디언트에 대한 코드를 절약하고 임계값을 낮출 수 있습니다.


연구소의 3D 분류 실험에서 두 번째 데이터셋은 본 실습의 데이터셋과 매우 유사하므로 들어가셔서 운용하시면 됩니다.