개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
InSection 20.1, we introduced the basic ideas behind how GANs work. We showed that they can draw samples from some simple, easy-to-sample distribution, like a uniform or normal distribution, and transform them into samples that appear to match the distribution of some dataset. And while our example of matching a 2D Gaussian distribution got the point across, it is not especially exciting.
섹션 20.1에서 GAN 작동 방식에 대한 기본 아이디어를 소개했습니다. 우리는 균일 분포 또는 정규 분포와 같이 간단하고 샘플링하기 쉬운 분포에서 샘플을 추출하여 일부 데이터 세트의 분포와 일치하는 것처럼 보이는 샘플로 변환할 수 있음을 보여주었습니다. 2D 가우스 분포를 일치시키는 예제는 요점을 이해했지만 특별히 흥미롭지는 않습니다.
In this section, we will demonstrate how you can use GANs to generate photorealistic images. We will be basing our models on the deep convolutional GANs (DCGAN) introduced inRadfordet al.(2015). We will borrow the convolutional architecture that have proven so successful for discriminative computer vision problems and show how via GANs, they can be leveraged to generate photorealistic images.
이 섹션에서는 GAN을 사용하여 사실적인 이미지를 생성하는 방법을 보여줍니다. 우리는 Radford 등이 소개한 DCGAN(Deep Convolutional GAN)을 모델의 기반으로 삼을 것입니다. (2015). 우리는 차별적인 컴퓨터 비전 문제에 대해 매우 성공적인 것으로 입증된 컨벌루션 아키텍처를 빌려 GAN을 통해 어떻게 활용하여 사실적인 이미지를 생성할 수 있는지 보여줄 것입니다.
import warnings
import torch
import torchvision
from torch import nn
from d2l import torch as d2l
import warnings: 경고 메시지를 관리하기 위한 파이썬 내장 모듈인 warnings를 가져옵니다. 이 모듈은 경고를 표시하거나 숨기는 데 사용됩니다.
import torch: 파이토치(PyTorch) 라이브러리를 가져옵니다. 파이토치는 딥러닝 모델을 구축하고 훈련하기 위한 인기 있는 라이브러리입니다.
import torchvision: 파이토치와 함께 제공되는 torchvision 라이브러리를 가져옵니다. torchvision은 컴퓨터 비전 작업을 위한 데이터셋, 모델 아키텍처, 변환 등을 제공합니다.
from torch import nn: 파이토치의 nn 모듈에서 Neural Network 모델과 관련된 클래스 및 함수를 가져옵니다. 이 모듈을 사용하여 다양한 뉴럴 네트워크 레이어를 정의하고 모델을 구성할 수 있습니다.
from d2l import torch as d2l: D2L 라이브러리에서 파이토치 관련 기능을 가져옵니다. D2L은 Dive into Deep Learning 책의 학습 자료와 함께 제공되는 라이브러리로, 딥러닝 모델의 이해와 구현을 돕는 데 사용됩니다.
이 코드는 딥러닝 모델을 구축하고 훈련하기 위해 필요한 라이브러리와 모듈을 가져오는 부분으로, 이후의 코드에서 이러한 라이브러리와 모듈을 사용하여 모델을 구현하고 훈련할 것입니다.
20.2.1.The Pokemon Dataset
The dataset we will use is a collection of Pokemon sprites obtained frompokemondb. First download, extract and load this dataset.
우리가 사용할 데이터 세트는 pokemondb에서 얻은 포켓몬 스프라이트 모음입니다. 먼저 이 데이터세트를 다운로드하고 추출하고 로드하세요.
d2l.DATA_HUB['pokemon'] = (d2l.DATA_URL + 'pokemon.zip', 'c065c0e2593b8b161a2d7873e42418bf6a21106c'): 이 코드 라인은 D2L 라이브러리의 데이터 허브(DATA_HUB)에 Pokemon 데이터셋의 URL과 해당 데이터의 해시 값을 등록합니다. 이를 통해 데이터를 다운로드하고 검증할 수 있습니다.
data_dir = d2l.download_extract('pokemon'): d2l.download_extract 함수를 사용하여 Pokemon 데이터셋을 다운로드하고 압축을 해제한 후, 압축 해제된 데이터의 디렉토리 경로를 data_dir 변수에 저장합니다.
pokemon = torchvision.datasets.ImageFolder(data_dir): 파이토치(torchvision)의 ImageFolder 데이터셋 클래스를 사용하여 data_dir에서 이미지 데이터를 읽어옵니다. 이 클래스는 이미지 데이터를 클래스별로 정리한 폴더 구조에서 데이터를 읽어옵니다. 예를 들어, 각 폴더는 하나의 클래스(라벨)를 나타내며 해당 클래스에 속하는 이미지 파일들을 해당 폴더에 저장합니다.
이 코드를 실행하면 Pokemon 데이터셋이 다운로드되고 파이토치 데이터셋 객체인 pokemon에 로드됩니다. 이후에는 이 데이터셋을 사용하여 딥러닝 모델을 학습하고 분류 등의 작업을 수행할 수 있습니다.
Downloading ../data/pokemon.zip from http://d2l-data.s3-accelerate.amazonaws.com/pokemon.zip...
We resize each image into64×64. TheToTensortransformation will project the pixel value into[0,1], while our generator will use the tanh function to obtain outputs in[−1,1]. Therefore we normalize the data with0.5mean and0.5standard deviation to match the value range.
각 이미지의 크기를 64×64로 조정합니다. ToTensor 변환은 픽셀 값을 [0,1]에 투영하는 반면 생성기는 tanh 함수를 사용하여 [-1,1]의 출력을 얻습니다. 따라서 값 범위와 일치하도록 평균 0.5, 표준편차 0.5로 데이터를 정규화합니다.
pokemon.transform = transformer: Pokemon 데이터셋의 전체 데이터에 대해 위에서 정의한 transformer를 적용합니다. 이제 데이터셋 내의 모든 이미지는 위의 변환을 거치게 됩니다.
data_iter = torch.utils.data.DataLoader(pokemon, batch_size=batch_size, shuffle=True, num_workers=d2l.get_dataloader_workers()): torch.utils.data.DataLoader를 사용하여 데이터셋을 미니배치로 나누고 데이터 로딩을 관리합니다.
pokemon: 전처리된 Pokemon 데이터셋입니다.
batch_size: 미니배치의 크기를 설정합니다.
shuffle=True: 데이터를 에포크마다 섞습니다. 이것은 모델이 각 에포크에서 다양한 데이터를 볼 수 있도록 도와줍니다.
num_workers=d2l.get_dataloader_workers(): 데이터 로딩을 병렬로 처리할 워커(worker) 수를 설정합니다. 이렇게 하면 데이터 로딩이 빨라지며 훈련 속도를 향상시킬 수 있습니다.
이 코드를 실행하면 Pokemon 데이터셋이 전처리되고 미니배치 단위로 모델에 공급될 수 있는 형태로 준비됩니다. 이 데이터로더를 사용하여 훈련 및 검증 과정에서 모델을 훈련하고 테스트할 수 있습니다.
Let’s visualize the first 20 images.
처음 20개의 이미지를 시각화해 보겠습니다.
warnings.filterwarnings('ignore')
d2l.set_figsize((4, 4))
for X, y in data_iter:
imgs = X[:20,:,:,:].permute(0, 2, 3, 1)/2+0.5
d2l.show_images(imgs, num_rows=4, num_cols=5)
break
warnings.filterwarnings('ignore'): 이 코드 라인은 경고 메시지를 무시하도록 설정합니다. 데이터셋을 시각화할 때 발생할 수 있는 경고 메시지를 표시하지 않도록 합니다.
d2l.set_figsize((4, 4)): 시각화된 이미지의 크기를 설정합니다. 이 경우에는 (4, 4) 크기로 설정되었습니다.
for X, y in data_iter:: data_iter 데이터 로더를 통해 미니배치를 순회합니다. X는 이미지 데이터를, y는 해당 이미지의 라벨을 나타냅니다.
imgs = X[:20,:,:,:].permute(0, 2, 3, 1)/2+0.5: 첫 번째 미니배치 중에서 처음 20개의 이미지 데이터를 선택하고, 차원 순서를 변경합니다. 원래 이미지의 차원 순서는 (배치 크기, 채널 수, 높이, 너비)이지만, 여기서는 (배치 크기, 높이, 너비, 채널 수)로 변경됩니다. 또한, 이미지의 픽셀값을 0.5를 더하고 2로 나누어 스케일을 조정합니다. 이로써 이미지의 픽셀값이 [0, 1] 범위로 스케일링됩니다.
d2l.show_images(imgs, num_rows=4, num_cols=5): d2l.show_images 함수를 사용하여 이미지를 시각화합니다. imgs는 이미지 데이터의 배치를 나타내며, num_rows와 num_cols는 행과 열의 개수를 설정합니다. 이 경우에는 4x5 격자로 이미지가 표시됩니다.
break: 첫 번째 미니배치를 시각화하고 나서 반복문을 종료합니다. 이 코드는 시각적으로 데이터셋의 일부를 확인할 수 있도록 도와줍니다.
이 코드를 실행하면 Pokemon 데이터셋에서 선택한 이미지 샘플이 시각화되어 출력됩니다. 이를 통해 데이터셋의 내용을 확인할 수 있습니다.
20.2.2.The Generator
The generator needs to map the noise variablez∈ℝ**d, a length-dvector, to a RGB image with width and height to be64×64. InSection 14.11we introduced the fully convolutional network that uses transposed convolution layer (refer toSection 14.10) to enlarge input size. The basic block of the generator contains a transposed convolution layer followed by the batch normalization and ReLU activation.
생성기는 길이 d 벡터인 노이즈 변수 z∈ℝ**d를 너비와 높이가 64×64인 RGB 이미지에 매핑해야 합니다. 섹션 14.11에서 우리는 입력 크기를 확대하기 위해 전치 컨볼루션 레이어(섹션 14.10 참조)를 사용하는 완전 컨볼루션 네트워크를 소개했습니다. 생성기의 기본 블록에는 배치 정규화 및 ReLU 활성화가 뒤따르는 전치된 컨볼루션 레이어가 포함되어 있습니다.
이 클래스는 생성자 네트워크에서 사용되는 하나의 블록을 정의합니다. 블록은 ConvTranspose2d 레이어, BatchNorm2d 레이어, 그리고 ReLU 활성화 함수로 구성됩니다.
__init__ 메서드: 블록의 초기화를 담당합니다. 다양한 매개변수를 사용하여 ConvTranspose2d 레이어, BatchNorm2d 레이어, ReLU 활성화 함수를 생성합니다. 이 때, out_channels는 출력 채널 수, in_channels는 입력 채널 수, kernel_size는 컨볼루션 커널의 크기, strides는 스트라이드 값, padding은 패딩 값 등을 설정할 수 있습니다.
forward 메서드: 순전파를 정의합니다. 입력 데이터 X를 ConvTranspose2d 레이어, BatchNorm2d 레이어, ReLU 활성화 함수의 순서로 전파하여 출력을 반환합니다.
이 클래스를 사용하여 생성자 네트워크를 구축할 때, GAN 모델의 생성자는 여러 개의 G_block 레이어를 쌓아서 이미지를 생성합니다. 이러한 블록을 적절하게 조합하여 원하는 이미지를 생성하는 네트워크를 만들 수 있습니다.
In default, the transposed convolution layer uses akℎ=kw=4kernel, asℎ=sw=2strides, and a Pℎ=Pw=1padding. With a input shape ofn'h×n'w=16×16, the generator block will double input’s width and height.
기본적으로 전치 컨볼루션 레이어는 kℎ=kw=4 커널, sℎ=sw=2 스트라이드 및 Pℎ=Pw=1 패딩을 사용합니다. n'h×n'w=16×16의 입력 형태를 사용하면 생성기 블록은 입력의 너비와 높이를 두 배로 늘립니다.
x = torch.zeros((2, 3, 16, 16))
g_blk = G_block(20)
g_blk(x).shape
x = torch.zeros((2, 3, 16, 16)): 2개의 샘플(batch size가 2), 3개의 채널(channel), 각각 16x16 크기의 이미지를 나타내는 4D 텐서를 생성합니다. 이 텐서는 가짜 이미지 생성을 위한 입력 데이터로 사용됩니다.
g_blk = G_block(20): G_block 클래스를 사용하여 생성자 네트워크의 블록을 하나 생성합니다. 이 블록은 3D 텐서를 입력으로 받아 처리하고, 출력으로 3D 텐서를 생성합니다. 여기서 out_channels를 20으로 설정하여 출력 채널의 수를 20으로 정의합니다.
g_blk(x).shape: 생성자 블록 g_blk에 입력 데이터 x를 전달하여 가짜 이미지를 생성합니다. 그리고 생성된 가짜 이미지의 크기(shape)를 확인합니다. 생성된 이미지의 크기를 확인하는 이유는 네트워크의 출력 크기를 이해하고 모델을 구성하기 위함입니다.
코드를 실행하면 g_blk를 사용하여 입력 데이터 x를 처리한 결과로 생성된 가짜 이미지의 크기(shape)를 확인할 수 있습니다. 이 예시에서는 출력 크기가 어떻게 결정되는지를 보여주기 위한 것이며, 실제 GAN 모델에서는 여러 개의 G_block 레이어를 조합하여 높은 해상도의 이미지를 생성하게 됩니다.
torch.Size([2, 20, 32, 32])
If changing the transposed convolution layer to a4×4kernel,1×1strides and zero padding. With a input size of1×1, the output will have its width and height increased by 3 respectively.
전치된 컨볼루션 레이어를 4×4 커널로 변경하면 스트라이드는 1×1이고 패딩은 0입니다. 입력 크기가 1×1이면 출력의 너비와 높이가 각각 3씩 증가합니다.
코드는 생성자 네트워크의 G_block 클래스를 사용하여 가짜 이미지를 생성하는 예시를 더 자세히 설명합니다. 이번에는 스트라이드(strides)와 패딩(padding)을 다르게 설정하여 어떻게 영향을 미치는지를 보여줍니다.
x = torch.zeros((2, 3, 1, 1)): 2개의 샘플(batch size가 2), 3개의 채널(channel), 각각 1x1 크기의 이미지를 나타내는 4D 텐서를 생성합니다. 이 텐서는 가짜 이미지 생성을 위한 입력 데이터로 사용됩니다. 이번에는 이미지 크기가 1x1로 매우 작습니다.
g_blk = G_block(20, strides=1, padding=0): G_block 클래스를 사용하여 생성자 네트워크의 블록을 하나 생성합니다. 이 블록은 스트라이드(strides)를 1로, 패딩(padding)을 0으로 설정하여 생성됩니다. 이렇게 설정하면 출력 이미지의 크기가 입력 이미지와 동일하게 유지됩니다. 출력 채널의 수는 20으로 설정됩니다.
g_blk(x).shape: 생성자 블록 g_blk에 입력 데이터 x를 전달하여 가짜 이미지를 생성합니다. 그리고 생성된 가짜 이미지의 크기(shape)를 확인합니다.
이번 예시에서는 스트라이드와 패딩을 1과 0으로 설정하여 출력 이미지의 크기가 입력 이미지와 동일하게 유지되었습니다. 따라서 출력 이미지의 크기는 여전히 1x1입니다. 이렇게 설정하면 이미지의 공간 해상도가 유지되면서 채널 수가 늘어나는 효과가 있습니다. 스트라이드와 패딩을 조절하여 생성자 네트워크의 출력 이미지 크기를 조절할 수 있습니다.
torch.Size([2, 20, 4, 4])
The generator consists of four basic blocks that increase input’s both width and height from 1 to 32. At the same time, it first projects the latent variable into64×8channels, and then halve the channels each time. At last, a transposed convolution layer is used to generate the output. It further doubles the width and height to match the desired64×64shape, and reduces the channel size to3. The tanh activation function is applied to project output values into the(−1,1)range.
생성기는 입력의 너비와 높이를 1에서 32로 증가시키는 4개의 기본 블록으로 구성됩니다. 동시에 잠재 변수를 먼저 64×8 채널로 투영한 다음 매번 채널을 절반으로 줄입니다. 마지막으로, 전치된 컨볼루션 레이어가 출력을 생성하는 데 사용됩니다. 원하는 64×64 모양과 일치하도록 너비와 높이를 추가로 두 배로 늘리고 채널 크기를 3으로 줄입니다. tanh 활성화 함수는 출력 값을 (-1,1) 범위로 투영하는 데 적용됩니다.
생성자 네트워크인 net_G를 정의하는 부분으로, GAN (Generative Adversarial Network) 모델에서 사용됩니다. 이 코드는 생성자 네트워크를 구성하고 각 블록의 출력 크기를 주석으로 설명하고 있습니다.
n_G = 64: n_G는 생성자 네트워크에서 사용할 초기 채널 수를 나타냅니다. 이 값은 64로 설정되었습니다.
net_G = nn.Sequential(...): 생성자 네트워크를 정의하는 nn.Sequential 컨테이너를 생성합니다. 이 컨테이너는 여러 레이어를 순차적으로 쌓을 수 있도록 합니다.
G_block(...) 블록들: 생성자 네트워크는 G_block 클래스를 사용하여 여러 개의 블록으로 구성됩니다. 각 블록은 생성자 네트워크의 한 단계를 나타내며, 입력 데이터의 차원을 높이거나 채널 수를 줄이는 역할을 합니다. 주석으로 출력 크기를 표시했습니다. 예를 들어, 첫 번째 블록은 100차원의 랜덤 노이즈 벡터를 입력으로 받고, 출력으로 (64 * 8, 4, 4) 크기의 텐서를 생성합니다.
nn.ConvTranspose2d(...) 레이어: 마지막에는 nn.ConvTranspose2d 레이어를 사용하여 최종 출력 이미지를 생성합니다. 이 레이어는 입력 이미지의 크기를 확대하고, 출력 채널 수를 3으로 설정하여 컬러 이미지를 생성합니다.
nn.Tanh(): 마지막으로, Tanh 활성화 함수를 사용하여 출력 이미지의 픽셀 값을 [-1, 1] 범위로 조정합니다.
이렇게 정의된 net_G는 생성자 네트워크를 나타내며, 랜덤 노이즈 벡터로부터 실제 이미지와 유사한 가짜 이미지를 생성하는 역할을 합니다.
Generate a 100 dimensional latent variable to verify the generator’s output shape.
생성기의 출력 형태를 검증하기 위해 100차원 잠재변수를 생성합니다.
x = torch.zeros((1, 100, 1, 1))
net_G(x).shape
생성자 네트워크인 net_G에 랜덤 노이즈 벡터를 입력으로 주고, 이를 이용하여 가짜 이미지를 생성한 후, 생성된 이미지의 크기(shape)를 확인하는 예시를 보여줍니다.
x = torch.zeros((1, 100, 1, 1)): 크기가 (1, 100, 1, 1)인 4D 텐서를 생성합니다. 이 텐서는 생성자 네트워크 net_G의 입력으로 사용될 랜덤 노이즈 벡터를 나타냅니다. 이 랜덤 노이즈 벡터는 100차원이며, 크기가 1x1인 가짜 이미지를 생성하기 위한 초기 입력으로 사용됩니다.
net_G(x).shape: 생성자 네트워크 net_G에 랜덤 노이즈 벡터 x를 전달하여 가짜 이미지를 생성합니다. 그리고 생성된 가짜 이미지의 크기(shape)를 확인합니다.
코드를 실행하면 랜덤 노이즈 벡터를 입력으로 사용하여 생성자 네트워크가 가짜 이미지를 생성하고, 이 이미지의 크기(shape)를 확인합니다. 실제로 실행할 때마다 다른 랜덤한 이미지가 생성됩니다. 이것은 GAN 모델에서 생성자의 역할로 사용되며, 생성자는 학습을 통해 실제 이미지와 유사한 가짜 이미지를 생성하는 능력을 향상시킵니다.
torch.Size([1, 3, 64, 64])
20.2.3.Discriminator
The discriminator is a normal convolutional network network except that it uses a leaky ReLU as its activation function. Givenα∈[0,1], its definition is
판별자는 활성화 함수로 Leaky ReLU를 사용한다는 점을 제외하면 일반적인 컨벌루션 네트워크 네트워크입니다. α∈[0,1]이 주어지면 그 정의는 다음과 같습니다.
As it can be seen, it is normal ReLU ifα=0, and an identity function ifα=1. Forα∈(0,1), leaky ReLU is a nonlinear function that give a non-zero output for a negative input. It aims to fix the “dying ReLU” problem that a neuron might always output a negative value and therefore cannot make any progress since the gradient of ReLU is 0.
보시다시피, α=0이면 일반 ReLU이고, α=1이면 항등함수입니다. α∈(0,1)의 경우 Leaky ReLU는 음수 입력에 대해 0이 아닌 출력을 제공하는 비선형 함수입니다. 뉴런이 항상 음수 값을 출력할 수 있고 ReLU의 기울기가 0이기 때문에 어떤 진전도 할 수 없는 "죽어가는 ReLU" 문제를 해결하는 것이 목표입니다.
alphas = [0, .2, .4, .6, .8, 1]
x = torch.arange(-2, 1, 0.1)
Y = [nn.LeakyReLU(alpha)(x).detach().numpy() for alpha in alphas]
d2l.plot(x.detach().numpy(), Y, 'x', 'y', alphas)
다양한 Leaky ReLU 활성화 함수에서의 출력을 시각화하는 예시를 보여줍니다.
alphas = [0, .2, .4, .6, .8, 1]: Leaky ReLU 활성화 함수에 사용될 alpha 값을 리스트로 정의합니다. alpha 값은 Leaky ReLU 함수에서 음수 입력에 대한 출력의 기울기를 나타냅니다. 여기서는 다양한 alpha 값을 실험해보고 시각화합니다.
x = torch.arange(-2, 1, 0.1): -2부터 1까지 0.1 간격으로 숫자를 생성하여 x에 저장합니다. 이 범위의 숫자는 Leaky ReLU 함수에 입력으로 사용됩니다.
Y = [nn.LeakyReLU(alpha)(x).detach().numpy() for alpha in alphas]: 각 alpha 값에 대해 Leaky ReLU 활성화 함수를 x에 적용하고 결과를 Y 리스트에 저장합니다. detach().numpy()를 사용하여 PyTorch 텐서를 넘파이 배열로 변환합니다.
d2l.plot(x.detach().numpy(), Y, 'x', 'y', alphas): d2l.plot 함수를 사용하여 결과를 시각화합니다. x 축은 입력 x의 값, y 축은 Leaky ReLU 함수의 출력 값입니다. 각 alpha 값에 해당하는 곡선이 다른 색상으로 표시됩니다. 이를 통해 Leaky ReLU 활성화 함수의 alpha 값이 변할 때 어떻게 출력이 달라지는지를 시각적으로 확인할 수 있습니다.
코드를 실행하면 다양한 alpha 값에 대한 Leaky ReLU 함수의 출력을 시각화한 그래프가 표시됩니다. alpha 값이 커질수록 입력의 음수 부분에 대한 출력이 크게 유지되는 것을 관찰할 수 있습니다. 이를 통해 Leaky ReLU의 역할을 이해할 수 있습니다.
The basic block of the discriminator is a convolution layer followed by a batch normalization layer and a leaky ReLU activation. The hyperparameters of the convolution layer are similar to the transpose convolution layer in the generator block.
판별기의 기본 블록은 컨볼루션 계층과 그 뒤에 배치 정규화 계층 및 누출된 ReLU 활성화로 구성됩니다. 컨볼루션 레이어의 하이퍼파라미터는 생성기 블록의 전치 컨볼루션 레이어와 유사합니다.
alpha: Leaky ReLU 활성화 함수에서 사용되는 negative slope 값을 나타냅니다. 기본값은 0.2로, 일반적으로 사용되는 값입니다.
이 클래스의 forward 메서드에서는 다음과 같은 작업을 수행합니다:
self.conv2d(X): 입력 데이터 X에 대해 컨볼루션 연산을 수행합니다. 이 연산은 채널 수와 커널 크기에 따라 출력 텐서를 생성합니다.
self.batch_norm(...): 컨볼루션 연산의 결과에 배치 정규화를 적용합니다. 이는 네트워크의 안정성과 학습 속도를 향상시키는 데 도움을 줍니다.
self.activation(...): 배치 정규화를 거친 결과에 Leaky ReLU 활성화 함수를 적용합니다. Leaky ReLU는 양수 값은 그대로 두고 음수 값에 작은 기울기를 적용하는 함수로, GAN에서 주로 사용됩니다.
이 클래스를 사용하면 판별자 네트워크에서 한 블록을 구성하고, 여러 개의 이러한 블록을 조합하여 전체 판별자 네트워크를 구축할 수 있습니다. 이러한 블록을 사용하여 이미지의 특징을 추출하고 진짜와 가짜 이미지를 구분하는 역할을 수행합니다.
A basic block with default settings will halve the width and height of the inputs, as we demonstrated inSection 7.3. For example, given a input shapenℎ=nw=16, with a kernel shapekℎ=kw=4, a stride shape sℎ=sw=2, and a padding shapepℎ=pw=1, the output shape will be:
섹션 7.3에서 설명한 것처럼 기본 설정이 있는 기본 블록은 입력의 너비와 높이를 절반으로 줄입니다. 예를 들어 입력 형태 nℎ=nw=16, 커널 형태 kℎ=kw=4, 스트라이드 형태 sℎ=sw=2, 패딩 형태 pℎ=pw=1이 있는 경우 출력 형태는 다음과 같습니다.
x = torch.zeros((2, 3, 16, 16))
d_blk = D_block(20)
d_blk(x).shape
판별자 네트워크의 D_block 클래스를 사용하여 가짜 이미지에 대한 판별 결과를 계산하는 예시를 보여줍니다.
x = torch.zeros((2, 3, 16, 16)): 2개의 샘플(batch size가 2), 3개의 채널(channel), 각각 16x16 크기의 이미지를 나타내는 4D 텐서를 생성합니다. 이 텐서는 판별자 네트워크 d_blk에 대한 입력 데이터로 사용됩니다.
d_blk = D_block(20): D_block 클래스를 사용하여 판별자 네트워크의 블록을 하나 생성합니다. 이 블록은 3D 텐서를 입력으로 받아 처리하고, 출력으로 판별 결과를 나타내는 텐서를 생성합니다. 여기서 out_channels를 20으로 설정하여 출력 채널의 수를 20으로 정의합니다.
d_blk(x).shape: 판별자 블록 d_blk에 입력 데이터 x를 전달하여 가짜 이미지에 대한 판별 결과를 계산합니다. 그리고 판별 결과의 크기(shape)를 확인합니다.
코드를 실행하면 가짜 이미지에 대한 판별 결과가 계산되고, 이 결과의 크기(shape)가 반환됩니다. 판별자 네트워크는 입력 이미지에 대한 판별 결과를 출력하며, 이를 통해 가짜 이미지와 실제 이미지를 구별합니다.
torch.Size([2, 20, 8, 8])
A basic block with default settings will halve the width and height of the inputs, as we demonstrated inSection 7.3. For example, given a input shapenℎ=nw=16, with a kernel shapekℎ=kw=4, a stride shapesℎ=sw=2, and a padding shapepℎ=pw=1, the output shape will be:
섹션 7.3에서 설명한 것처럼 기본 설정이 있는 기본 블록은 입력의 너비와 높이를 절반으로 줄입니다. 예를 들어 입력 형태 nℎ=nw=16, 커널 형태 kℎ=kw=4, 스트라이드 형태 sℎ=sw=2, 패딩 형태 pℎ=pw=1이 있는 경우 출력 형태는 다음과 같습니다.
x = torch.zeros((2, 3, 16, 16))
d_blk = D_block(20)
d_blk(x).shape
판별자 네트워크의 D_block 클래스를 사용하여 가짜 이미지에 대한 판별 결과를 계산하는 예시를 보여줍니다.
x = torch.zeros((2, 3, 16, 16)): 2개의 샘플(batch size가 2), 3개의 채널(channel), 각각 16x16 크기의 이미지를 나타내는 4D 텐서를 생성합니다. 이 텐서는 판별자 네트워크 d_blk에 대한 입력 데이터로 사용됩니다.
d_blk = D_block(20): D_block 클래스를 사용하여 판별자 네트워크의 블록을 하나 생성합니다. 이 블록은 3D 텐서를 입력으로 받아 처리하고, 출력으로 판별 결과를 나타내는 텐서를 생성합니다. 여기서 out_channels를 20으로 설정하여 출력 채널의 수를 20으로 정의합니다.
d_blk(x).shape: 판별자 블록 d_blk에 입력 데이터 x를 전달하여 가짜 이미지에 대한 판별 결과를 계산합니다. 그리고 판별 결과의 크기(shape)를 확인합니다.
코드를 실행하면 가짜 이미지에 대한 판별 결과가 계산되고, 이 결과의 크기(shape)가 반환됩니다. 판별자 네트워크는 입력 이미지에 대한 판별 결과를 출력하며, 이를 통해 가짜 이미지와 실제 이미지를 구별합니다. 이 코드는 판별자 블록이 입력 이미지를 어떻게 처리하고 판별 결과를 출력하는지를 보여주는 예시입니다.
판별자 네트워크인 net_D를 정의하는 부분으로, GAN (Generative Adversarial Network) 모델에서 사용됩니다. 이 코드는 판별자 네트워크를 구성하고 각 블록의 출력 크기를 주석으로 설명하고 있습니다.
n_D = 64: n_D는 판별자 네트워크에서 사용할 초기 채널 수를 나타냅니다. 이 값은 64로 설정되었습니다.
net_D = nn.Sequential(...): 판별자 네트워크를 정의하는 nn.Sequential 컨테이너를 생성합니다. 이 컨테이너는 여러 레이어를 순차적으로 쌓을 수 있도록 합니다.
D_block(...) 블록들: 판별자 네트워크는 D_block 클래스를 사용하여 여러 개의 블록으로 구성됩니다. 각 블록은 판별자 네트워크의 한 단계를 나타내며, 입력 데이터의 차원을 줄이거나 채널 수를 늘리는 역할을 합니다. 주석으로 출력 크기를 표시했습니다. 예를 들어, 첫 번째 블록은 (64, 32, 32) 크기의 텐서를 입력으로 받고, 출력으로 (64, 32, 32) 크기의 텐서를 생성합니다.
nn.Conv2d(...) 레이어: 마지막에는 nn.Conv2d 레이어를 사용하여 최종 판별 결과를 생성합니다. 이 레이어는 입력 이미지를 판별한 결과를 출력하며, 출력 채널 수는 1로 설정되어 이진 분류 결과를 나타냅니다.
이렇게 정의된 net_D는 판별자 네트워크를 나타내며, 입력 이미지를 판별하여 이미지가 진짜인지 가짜인지를 판별하는 역할을 합니다. 판별자 네트워크는 이미지의 공간적 정보를 이용하여 판별 결과를 계산합니다.
It uses a convolution layer with output channel1as the last layer to obtain a single prediction value.
단일 예측 값을 얻기 위해 출력 채널 1이 있는 컨볼루션 레이어를 마지막 레이어로 사용합니다.
x = torch.zeros((1, 3, 64, 64))
net_D(x).shape
판별자 네트워크인 net_D에 입력 데이터를 전달하고, 판별 결과의 크기(shape)를 확인하는 예시를 보여줍니다.
x = torch.zeros((1, 3, 64, 64)): 크기가 (1, 3, 64, 64)인 4D 텐서를 생성합니다. 이 텐서는 판별자 네트워크 net_D에 입력 데이터로 사용됩니다. 여기서 크기 (1, 3, 64, 64)은 배치 크기가 1이고, 채널 수가 3 (RGB 이미지)이며, 이미지 크기가 64x64임을 의미합니다.
net_D(x).shape: 생성된 입력 데이터 x를 판별자 네트워크 net_D에 전달하여 판별 결과를 계산합니다. 그리고 판별 결과의 크기(shape)를 확인합니다.
코드를 실행하면 판별자 네트워크가 입력 이미지 x를 판별하고, 판별 결과의 크기(shape)가 반환됩니다. 판별자는 입력 이미지를 받아 이미지가 진짜인지 가짜인지 판별하는 역할을 수행합니다. 이 코드는 판별자 네트워크의 출력을 확인하는 간단한 예시입니다.
torch.Size([1, 1, 1, 1])
20.2.4.Training
Compared to the basic GAN inSection 20.1, we use the same learning rate for both generator and discriminator since they are similar to each other. In addition, we changeβ1in Adam (Section 12.10) from0.9to0.5. It decreases the smoothness of the momentum, the exponentially weighted moving average of past gradients, to take care of the rapid changing gradients because the generator and the discriminator fight with each other. Besides, the random generated noiseZ, is a 4-D tensor and we are using GPU to accelerate the computation.
20.1절의 기본 GAN과 비교하면 생성자와 판별자가 서로 유사하므로 동일한 학습률을 사용합니다. 또한 Adam(12.10절)의 β1을 0.9에서 0.5로 변경합니다. 생성자와 판별자가 서로 싸우기 때문에 빠르게 변화하는 기울기를 처리하기 위해 과거 기울기의 지수 가중 이동 평균인 운동량의 평활도를 감소시킵니다. 게다가 무작위로 생성된 노이즈 Z는 4차원 텐서이며 GPU를 사용하여 계산을 가속화합니다.
def train(net_D, net_G, data_iter, num_epochs, lr, latent_dim,
device=d2l.try_gpu()):
loss = nn.BCEWithLogitsLoss(reduction='sum')
for w in net_D.parameters():
nn.init.normal_(w, 0, 0.02)
for w in net_G.parameters():
nn.init.normal_(w, 0, 0.02)
net_D, net_G = net_D.to(device), net_G.to(device)
trainer_hp = {'lr': lr, 'betas': [0.5,0.999]}
trainer_D = torch.optim.Adam(net_D.parameters(), **trainer_hp)
trainer_G = torch.optim.Adam(net_G.parameters(), **trainer_hp)
animator = d2l.Animator(xlabel='epoch', ylabel='loss',
xlim=[1, num_epochs], nrows=2, figsize=(5, 5),
legend=['discriminator', 'generator'])
animator.fig.subplots_adjust(hspace=0.3)
for epoch in range(1, num_epochs + 1):
# Train one epoch
timer = d2l.Timer()
metric = d2l.Accumulator(3) # loss_D, loss_G, num_examples
for X, _ in data_iter:
batch_size = X.shape[0]
Z = torch.normal(0, 1, size=(batch_size, latent_dim, 1, 1))
X, Z = X.to(device), Z.to(device)
metric.add(d2l.update_D(X, Z, net_D, net_G, loss, trainer_D),
d2l.update_G(Z, net_D, net_G, loss, trainer_G),
batch_size)
# Show generated examples
Z = torch.normal(0, 1, size=(21, latent_dim, 1, 1), device=device)
# Normalize the synthetic data to N(0, 1)
fake_x = net_G(Z).permute(0, 2, 3, 1) / 2 + 0.5
imgs = torch.cat(
[torch.cat([
fake_x[i * 7 + j].cpu().detach() for j in range(7)], dim=1)
for i in range(len(fake_x)//7)], dim=0)
animator.axes[1].cla()
animator.axes[1].imshow(imgs)
# Show the losses
loss_D, loss_G = metric[0] / metric[2], metric[1] / metric[2]
animator.add(epoch, (loss_D, loss_G))
print(f'loss_D {loss_D:.3f}, loss_G {loss_G:.3f}, '
f'{metric[2] / timer.stop():.1f} examples/sec on {str(device)}')
GAN (Generative Adversarial Network) 모델의 학습 함수를 정의하고 있습니다. 이 함수는 판별자와 생성자 네트워크를 학습하는 역할을 합니다.
net_D와 net_G: 판별자 네트워크와 생성자 네트워크를 나타내는 모델 객체입니다.
data_iter: 데이터 로더로부터 생성된 데이터 배치를 나타내는 반복자입니다.
num_epochs: 학습할 epoch 수입니다.
lr: 학습률 (learning rate)입니다.
latent_dim: 생성자 네트워크의 입력 랜덤 벡터의 차원입니다.
device: 모델을 학습할 디바이스 (CPU 또는 GPU)를 나타냅니다.
loss = nn.BCEWithLogitsLoss(reduction='sum'): 이진 교차 엔트로피 손실 함수를 정의합니다. GAN에서는 이 손실 함수를 사용하여 판별자의 출력과 진짜/가짜 레이블 사이의 오차를 계산합니다.
모델 가중치 초기화: 생성자와 판별자 네트워크의 가중치를 초기화합니다.
trainer_D와 trainer_G: 판별자와 생성자 네트워크를 각각 최적화하기 위한 Adam 옵티마이저를 생성합니다.
animator: 학습 과정을 시각화하기 위한 d2l.Animator 객체를 생성합니다.
학습 루프: 지정된 epoch 수만큼 학습을 수행합니다. 각 epoch에서는 판별자와 생성자를 번갈아가며 학습하고, 학습 중간에 생성된 이미지를 시각화하여 학습 진행 상황을 확인합니다.
학습 속도 계산: 학습이 완료된 후, 판별자와 생성자의 손실과 학습 속도를 출력합니다.
이 함수는 GAN 모델을 학습하기 위한 핵심 학습 루프를 구현하고 있으며, 판별자와 생성자 네트워크의 학습을 번갈아가며 진행합니다. 학습이 진행됨에 따라 손실이 어떻게 변하는지 시각화하고, 학습 속도도 출력하여 모델의 학습 상태를 모니터링합니다.
We train the model with a small number of epochs just for demonstration. For better performance, the variablenum_epochscan be set to a larger number.
단지 시연을 위해 적은 수의 에포크로 모델을 훈련합니다. 더 나은 성능을 위해 num_epochs 변수를 더 큰 숫자로 설정할 수 있습니다.
num_epochs: 학습할 epoch 수를 나타냅니다. 여기서는 20으로 설정되었습니다.
train(net_D, net_G, data_iter, num_epochs, lr, latent_dim): 이전에 정의한 train 함수를 호출하여 GAN 모델을 학습합니다. 학습에 필요한 인자들을 함수에 전달합니다. 이렇게 하면 판별자와 생성자 네트워크가 데이터로부터 학습을 수행하고, 지정된 epoch 수만큼 학습이 진행됩니다.
이 코드는 GAN 모델을 실제 데이터로부터 학습시키는 부분을 실행하는 부분입니다. latent_dim, lr, num_epochs 등의 하이퍼파라미터를 설정하고, train 함수를 호출하여 학습을 시작합니다.
loss_D 0.023, loss_G 7.359, 2292.7 examples/sec on cuda:0
두번째 실행 결과
epochs를 50으로 했을 때
20.2.5.Summary
DCGAN architecture has four convolutional layers for the Discriminator and four “fractionally-strided” convolutional layers for the Generator.
The Discriminator is a 4-layer strided convolutions with batch normalization (except its input layer) and leaky ReLU activations.
Discriminator는 배치 정규화(입력 레이어 제외) 및 Leaky ReLU 활성화 기능을 갖춘 4레이어 스트라이드 컨볼루션입니다.
Leaky ReLU is a nonlinear function that give a non-zero output for a negative input. It aims to fix the “dying ReLU” problem and helps the gradients flow easier through the architecture.
Leaky ReLU는 음수 입력에 대해 0이 아닌 출력을 제공하는 비선형 함수입니다. 이는 "죽어가는 ReLU" 문제를 해결하는 것을 목표로 하며 아키텍처를 통해 그래디언트가 더 쉽게 흐르도록 돕습니다.
20.2.6.Exercises
What will happen if we use standard ReLU activation rather than leaky ReLU?
Apply DCGAN on Fashion-MNIST and see which category works well and which does not.
Throughout most of this book, we have talked about how to make predictions. In some form or another, we used deep neural networks to learn mappings from data examples to labels. This kind of learning is called discriminative learning, as in, we’d like to be able to discriminate between photos of cats and photos of dogs. Classifiers and regressors are both examples of discriminative learning. And neural networks trained by backpropagation have upended everything we thought we knew about discriminative learning on large complicated datasets. Classification accuracies on high-res images have gone from useless to human-level (with some caveats) in just 5-6 years. We will spare you another spiel about all the other discriminative tasks where deep neural networks do astoundingly well.
이 책의 대부분에서 우리는 예측하는 방법에 대해 이야기했습니다. 어떤 형태로든 우리는 심층 신경망을 사용하여 데이터 예제에서 레이블까지의 매핑을 학습했습니다. 이런 종류의 학습을 차별적 학습 discriminative learning 이라고 합니다. 고양이 사진과 개 사진을 구별할 수 있기를 원하기 때문입니다. 분류기 Classifiers와 회귀자 regressors는 모두 차별 학습 discriminative learning 의 예입니다. 그리고 역전파로 훈련된 신경망은 크고 복잡한 데이터 세트에 대한 차별적 학습에 대해 우리가 알고 있다고 생각했던 모든 것을 뒤집어 놓았습니다. 고해상도 이미지의 분류 정확도는 단 5~6년 만에 쓸모없는 수준에서 인간 수준(몇 가지 주의 사항 있음)으로 바뀌었습니다. 심층 신경망이 놀라울 정도로 잘 수행되는 다른 모든 식별 작업에 대해 더 이상 이야기하지 않겠습니다.
But there is more to machine learning than just solving discriminative tasks. For example, given a large dataset, without any labels, we might want to learn a model that concisely captures the characteristics of this data. Given such a model, we could sample synthetic data examples that resemble the distribution of the training data. For example, given a large corpus of photographs of faces, we might want to be able to generate a new photorealistic image that looks like it might plausibly have come from the same dataset. This kind of learning is called generative modeling.
그러나 머신러닝에는 단지 차별적 discriminative인 작업을 해결하는 것보다 더 많은 것이 있습니다. 예를 들어, 라벨이 없는 대규모 데이터 세트가 있으면 이 데이터의 특성을 간결하게 포착하는 모델을 학습하고 싶을 수 있습니다. 이러한 모델이 주어지면 훈련 데이터의 분포와 유사한 합성 데이터 예제를 샘플링할 수 있습니다. 예를 들어, 얼굴 사진으로 구성된 대규모 코퍼스가 있으면 동일한 데이터세트에서 나온 것처럼 보이는 새로운 사실적인 이미지를 생성할 수 있기를 원할 수 있습니다. 이러한 종류의 학습을 생성 모델링이라고 합니다.
Until recently, we had no method that could synthesize novel photorealistic images. But the success of deep neural networks for discriminative learning opened up new possibilities. One big trend over the last three years has been the application of discriminative deep nets to overcome challenges in problems that we do not generally think of as supervised learning problems. The recurrent neural network language models are one example of using a discriminative network (trained to predict the next character) that once trained can act as a generative model.
최근까지 우리는 새로운 사실적 이미지를 합성할 수 있는 방법이 없었습니다. 그러나 차별적 학습을 위한 심층 신경망의 성공은 새로운 가능성을 열어주었습니다. 지난 3년 동안의 큰 추세 중 하나는 일반적으로 지도 학습 문제로 생각하지 않는 문제를 극복하기 위해 차별적인 딥 넷을 적용한 것입니다. 순환 신경망 언어 모델은 일단 훈련되면 생성 모델 역할을 할 수 있는 식별 네트워크(다음 문자를 예측하도록 훈련됨)를 사용하는 한 가지 예입니다.
In 2014, a breakthrough paper introduced Generative adversarial networks (GANs)(Goodfellowet al., 2014), a clever new way to leverage the power of discriminative models to get good generative models. At their heart, GANs rely on the idea that a data generator is good if we cannot tell fake data apart from real data. In statistics, this is called a two-sample test - a test to answer the question whether datasetsX={x1,…,xn}andX′={x1′,…,x'n}were drawn from the same distribution. The main difference between most statistics papers and GANs is that the latter use this idea in a constructive way. In other words, rather than just training a model to say “hey, these two datasets do not look like they came from the same distribution”, they use thetwo-sample testto provide training signals to a generative model. This allows us to improve the data generator until it generates something that resembles the real data. At the very least, it needs to fool the classifier even if our classifier is a state of the art deep neural network.
2014년 획기적인 논문에서는 판별 모델의 힘을 활용하여 좋은 생성 모델을 얻는 영리하고 새로운 방법인 생성적 적대 네트워크(GAN)(Goodfellow et al., 2014)를 소개했습니다. GAN의 핵심은 실제 데이터와 가짜 데이터를 구별할 수 없다면 데이터 생성기가 좋다는 생각에 의존합니다. 통계에서는 이를 2-표본 검정이라고 합니다. 즉, 데이터 세트 X={x1,…,xn} 및 X′={x1′,…,x'n}이 동일한 분포에서 추출되었는지 여부에 대한 질문에 대답하는 테스트입니다. 대부분의 통계 논문과 GAN의 주요 차이점은 후자가 이 아이디어를 건설적인 방식으로 사용한다는 것입니다. 즉, 단순히 "이 두 데이터 세트는 동일한 분포에서 나온 것처럼 보이지 않습니다"라고 말하도록 모델을 훈련시키는 대신 2-샘플 테스트를 사용하여 생성 모델에 훈련 신호를 제공합니다. 이를 통해 실제 데이터와 유사한 것을 생성할 때까지 데이터 생성기를 개선할 수 있습니다. 최소한 분류기가 최첨단 심층 신경망이라 하더라도 분류기를 속일 필요는 있습니다.
The GAN architecture is illustrated inFig. 20.1.1. As you can see, there are two pieces in GAN architecture - first off, we need a device (say, a deep network but it really could be anything, such as a game rendering engine) that might potentially be able to generate data that looks just like the real thing. If we are dealing with images, this needs to generate images. If we are dealing with speech, it needs to generate audio sequences, and so on. We call this the generator network. The second component is the discriminator network. It attempts to distinguish fake and real data from each other. Both networks are in competition with each other. The generator network attempts to fool the discriminator network. At that point, the discriminator network adapts to the new fake data. This information, in turn is used to improve the generator network, and so on.
GAN 아키텍처는 그림 20.1.1에 설명되어 있습니다. 보시다시피, GAN 아키텍처에는 두 가지 부분이 있습니다. 먼저, 잠재적으로 보이는 데이터를 생성할 수 있는 장치(예: 심층 네트워크이지만 실제로는 게임 렌더링 엔진과 같은 모든 것이 될 수 있음)가 필요합니다. 진짜처럼. 이미지를 다루는 경우 이미지를 생성해야 합니다. 음성을 다루는 경우 오디오 시퀀스 등을 생성해야 합니다. 우리는 이것을 Generator 네트워크라고 부릅니다. 두 번째 구성 요소는 discriminator네트워크입니다. 가짜 데이터와 실제 데이터를 구별하려고 시도합니다. 두 네트워크는 서로 경쟁하고 있습니다. 생성자 네트워크는 판별자 discriminator 네트워크를 속이려고 시도합니다. 이 시점에서 판별기 네트워크는 새로운 가짜 데이터에 적응합니다. 이 정보는 generator network 등을 개선하는 데 사용됩니다.
The discriminator is a binary classifier to distinguish if the inputxis real (from real data) or fake (from the generator). Typically, the discriminator outputs a scalar predictiono∈ℝfor inputx, such as using a fully connected layer with hidden size 1, and then applies sigmoid function to obtain the predicted probabilityD(x)=1/(1+e**−o). Assume the labelyfor the true data is1and0for the fake data. We train the discriminator to minimize the cross-entropy loss,i.e.,
판별자는 입력 x가 실제(실제 데이터에서)인지 가짜(생성기에서)인지 구별하는 이진 분류기입니다. 일반적으로 판별기는 은닉 크기가 1인 완전 연결 레이어를 사용하는 것과 같이 입력 x에 대해 스칼라 예측 o∈ℝ을 출력합니다. 그런 다음 시그모이드 함수를 적용하여 예측 확률 'D(x)=1/(1+e**−o)'를 얻습니다. 실제 데이터의 레이블 y는 1이고 가짜 데이터의 레이블은 0이라고 가정합니다. 교차 엔트로피 손실을 최소화하기 위해 판별자를 훈련합니다. 즉,
For the generator, it first draws some parameterz∈ℝ**dfrom a source of randomness,e.g., a normal distributionz∼N(0,1). We often callzas the latent variable. It then applies a function to generatex′=G(z). The goal of the generator is to fool the discriminator to classifyx′=G(z)as true data,i.e., we wantD(G(z))≈1. In other words, for a given discriminatorD, we update the parameters of the generatorGto maximize the cross-entropy loss wheny=0,i.e.,
생성기의 경우 먼저 임의성의 소스(예: 정규 분포 z∼N(0,1))에서 일부 매개변수 z∈ℝ**d를 그립니다. 우리는 종종 z를 잠재 변수라고 부릅니다. 그런 다음 x′=G(z)를 생성하는 함수를 적용합니다. 생성기의 목표는 판별기를 속여 x′=G(z)를 실제 데이터로 분류하는 것입니다. 즉, D(G(z))≒1을 원합니다. 즉, 주어진 판별기 D에 대해 생성기 G의 매개변수를 업데이트하여 y=0일 때 교차 엔트로피 손실을 최대화합니다. 즉,
If the generator does a perfect job, thenD(x′)≈1, so the above loss is near 0, which results in the gradients that are too small to make good progress for the discriminator. So commonly, we minimize the following loss:
생성기가 완벽한 작업을 수행하면 D(x′) ≒1이므로 위의 손실은 0에 가까워서 판별기가 제대로 진행하기에는 기울기가 너무 작아집니다. 따라서 일반적으로 다음 손실을 최소화합니다.
which is just feedingx′=G(z)into the discriminator but giving labely=1.
x′=G(z)를 판별자에 입력하지만 라벨 y=1을 제공합니다.
To sum up,DandGare playing a “minimax” game with the comprehensive objective function:
요약하자면, D와 G는 포괄적인 목적 함수를 사용하여 "미니맥스" 게임을 하고 있습니다.
Many of the GANs applications are in the context of images. As a demonstration purpose, we are going to content ourselves with fitting a much simpler distribution first. We will illustrate what happens if we use GANs to build the world’s most inefficient estimator of parameters for a Gaussian. Let’s get started.
GAN 애플리케이션의 대부분은 이미지와 관련되어 있습니다. 데모 목적으로 먼저 훨씬 간단한 배포판을 맞추는 것으로 만족하겠습니다. GAN을 사용하여 세계에서 가장 비효율적인 가우스 매개변수 추정기를 구축하면 어떤 일이 발생하는지 설명하겠습니다. 시작하자.
%matplotlib inline
import torch
from torch import nn
from d2l import torch as d2l
%matplotlib inline: 이 코드 라인은 주피터 노트북 환경에서 그래프를 인라인으로 표시하도록 지정하는 명령입니다. 즉, 그래프가 노트북 안에서 바로 표시됩니다.
import torch: 파이토치 라이브러리를 임포트합니다. 파이토치는 딥 러닝 모델을 구축하고 학습하는 데 사용되는 라이브러리입니다.
from torch import nn: 파이토치의 nn 모듈에서 필요한 부분을 가져옵니다. nn 모듈은 신경망을 정의하고 학습하는 데 사용되는 다양한 도구와 클래스를 포함하고 있습니다.
from d2l import torch as d2l: "Dive into Deep Learning" 라이브러리에서 torch 모듈을 가져옵니다. 이 라이브러리는 교재와 관련된 유틸리티 함수와 도우미 함수를 제공합니다.
이 코드의 주요 목적은 파이토치와 "Dive into Deep Learning" 라이브러리를 설정하고 사용 가능한 도구와 기능을 가져오는 것입니다. 이러한 도구와 기능은 GAN을 구현하고 실험하는 데 사용될 것입니다.
20.1.1.Generate Some “Real” Data
Since this is going to be the world’s lamest example, we simply generate data drawn from a Gaussian.
이것은 세계에서 가장 형편없는 예가 될 것이기 때문에 우리는 단순히 가우스에서 가져온 데이터를 생성합니다.
X = torch.normal(0.0, 1, (1000, 2))
A = torch.tensor([[1, 2], [-0.1, 0.5]])
b = torch.tensor([1, 2])
data = torch.matmul(X, A) + b
X = torch.normal(0.0, 1, (1000, 2)): X는 평균이 0이고 표준 편차가 1인 정규 분포 (표준 정규 분포)에서 무작위로 샘플링된 값을 가지는 1000x2 크기의 텐서입니다. 이는 평균이 0이고 표준 편차가 1인 가우시안 분포에서 무작위로 데이터를 생성하는 것을 나타냅니다.
A = torch.tensor([[1, 2], [-0.1, 0.5]]): A는 2x2 크기의 텐서로, 행렬입니다. 이 행렬은 데이터에 곱해져서 변환을 수행하는 데 사용될 것입니다. 첫 번째 행은 [1, 2]이고 두 번째 행은 [-0.1, 0.5]입니다.
b = torch.tensor([1, 2]): b는 1x2 크기의 텐서로, 벡터입니다. 이 벡터는 데이터에 더해질 것이며, 각 차원에 대한 평행 이동을 나타냅니다. 첫 번째 요소는 1이고 두 번째 요소는 2입니다.
data = torch.matmul(X, A) + b: data는 행렬 X를 행렬 A로 변환하고 벡터 b를 더한 결과입니다. 이것은 선형 변환과 평행 이동을 나타내며, 데이터셋 data에 저장됩니다. 즉, X의 각 데이터 포인트에 대해 선형 변환과 평행 이동이 수행되어 최종 데이터셋이 생성됩니다.
이 코드는 데이터를 생성하는 과정을 보여주며, 이 데이터는 GAN 또는 다른 딥 러닝 모델을 학습하고 실험하는 데 사용될 수 있습니다.
Let’s see what we got. This should be a Gaussian shifted in some rather arbitrary way with meanband covariance matrixA**T A.
우리가 무엇을 얻었는지 봅시다. 이는 평균 b 및 공분산 행렬 A**T A를 사용하여 다소 임의적인 방식으로 이동된 가우스여야 합니다.
d2l.set_figsize(): 이 함수는 "Dive into Deep Learning" 라이브러리인 d2l을 사용하여 그림의 크기를 설정합니다. 이 코드에서는 그림 크기를 미리 설정하여 플롯을 만들 때 적절한 크기로 설정합니다.
d2l.plt.scatter(data[:100, (0)].detach().numpy(), data[:100, (1)].detach().numpy()): 이 코드는 데이터를 산점도로 표시합니다. data에서 처음 100개의 데이터 포인트에 대해 두 번째 차원과 세 번째 차원의 값을 가져와서 산점도를 그립니다. detach().numpy()는 텐서를 넘파이 배열로 변환하는 작업입니다.
print(f'The covariance matrix is\n{torch.matmul(A.T, A)}'): 이 코드는 주어진 행렬 A의 전치 행렬과 A 자체를 곱한 결과를 출력합니다. 이것은 데이터의 공분산 행렬을 나타내며, 출력 메시지에 포함됩니다.
이 코드는 데이터의 분포를 시각화하고 해당 데이터의 공분산을 계산하여 출력하는 역할을 합니다. 이를 통해 데이터의 특성과 분포를 파악할 수 있습니다.
The covariance matrix is
tensor([[1.0100, 1.9500],
[1.9500, 4.2500]])
batch_size = 8: 이 코드 라인은 미니배치의 크기를 8로 설정합니다. 미니배치는 한 번에 모델에 입력되는 데이터의 일부분을 나타냅니다. 여기서는 8개의 데이터 포인트로 구성된 미니배치를 사용하겠다는 것을 의미합니다.
data_iter = d2l.load_array((data,), batch_size): 이 코드 라인은 d2l 라이브러리의 load_array 함수를 사용하여 데이터를 미니배치로 나누는 데이터 반복자를 생성합니다. load_array 함수는 데이터를 가져와서 지정한 배치 크기로 나누고, 각 미니배치를 생성하는데 사용됩니다. 이 데이터 반복자(data_iter)를 사용하면 모델을 학습할 때 미니배치 단위로 데이터를 처리할 수 있습니다.
즉, 이 코드는 데이터를 작은 배치로 분할하고, 각 미니배치에 대한 반복자(data_iter)를 생성하는 과정을 나타냅니다. 이것은 모델 학습 및 평가에서 사용되는 일반적인 데이터 처리 방법 중 하나입니다.
20.1.2.Generator
Our generator network will be the simplest network possible - a single layer linear model. This is since we will be driving that linear network with a Gaussian data generator. Hence, it literally only needs to learn the parameters to fake things perfectly.
우리의 생성기 네트워크는 가능한 가장 간단한 네트워크, 즉 단일 레이어 선형 모델이 될 것입니다. 이는 가우스 데이터 생성기를 사용하여 선형 네트워크를 구동할 것이기 때문입니다. 따라서 말 그대로 완벽하게 가짜를 만들기 위한 매개변수만 학습하면 됩니다.
net_G = nn.Sequential(nn.Linear(2, 2))
nn.Sequential: 파이토치의 nn.Sequential은 뉴럴 네트워크의 일련의 연속적인 레이어를 정의하는 컨테이너입니다. 이 컨테이너를 사용하면 각 레이어를 순차적으로 쌓을 수 있습니다.
nn.Linear(2, 2): 이 부분은 nn.Sequential 내부에 추가될 첫 번째 레이어입니다. nn.Linear는 선형 변환을 수행하는 레이어로, 입력 차원과 출력 차원을 지정합니다. 여기서는 입력 차원이 2이고 출력 차원이 2인 선형 레이어를 정의합니다. 즉, 이 생성자 신경망은 2차원의 입력을 받아 2차원의 출력을 생성합니다.
이 코드는 간단한 생성자 신경망을 정의하는데 사용됩니다. GAN(Generative Adversarial Network)에서 생성자는 무작위 노이즈를 입력으로 받아 원하는 형태의 데이터를 생성하는 역할을 합니다. 이 코드에서는 입력 차원과 출력 차원이 모두 2로 설정되었으므로, 이 생성자는 2차원의 데이터를 생성하는 데 사용될 것입니다.
20.1.3.Discriminator
For the discriminator we will be a bit more discriminating: we will use an MLP with 3 layers to make things a bit more interesting.
판별자의 경우 좀 더 판별할 것입니다. 3개 레이어가 있는 MLP를 사용하여 좀 더 흥미롭게 만들 것입니다.
nn.Sequential: 파이토치의 nn.Sequential은 뉴럴 네트워크의 일련의 연속적인 레이어를 정의하는 컨테이너입니다. 이 컨테이너를 사용하면 각 레이어를 순차적으로 쌓을 수 있습니다.
nn.Linear(2, 5): 이 부분은 nn.Sequential 내부에 추가될 첫 번째 레이어입니다. nn.Linear는 선형 변환을 수행하는 레이어로, 입력 차원과 출력 차원을 지정합니다. 여기서는 입력 차원이 2이고 출력 차원이 5인 선형 레이어를 정의합니다. 이 레이어는 입력 데이터를 5차원 공간으로 변환합니다.
nn.Tanh(): 이는 하이퍼볼릭 탄젠트 활성화 함수를 나타냅니다. 이 활성화 함수는 레이어의 출력을 -1과 1 사이로 변환합니다.
이어지는 nn.Linear, nn.Tanh() 레이어들은 비슷한 방식으로 연결됩니다. 두 번째 레이어는 5차원을 3차원으로, 세 번째 레이어는 3차원을 1차원으로 변환합니다.
이 코드는 간단한 판별자 신경망을 정의하는데 사용됩니다. GAN(Generative Adversarial Network)에서 판별자는 생성된 데이터와 실제 데이터를 구분하는 역할을 합니다. 이 판별자는 2차원의 입력을 받아 하이퍼볼릭 탄젠트를 사용하여 비선형 변환을 수행하고, 여러 레이어를 통해 데이터를 1차원 출력으로 분류합니다.
20.1.4.Training
First we define a function to update the discriminator.
먼저 판별자를 업데이트하는 함수를 정의합니다.
#@save
def update_D(X, Z, net_D, net_G, loss, trainer_D):
"""Update discriminator."""
batch_size = X.shape[0]
ones = torch.ones((batch_size,), device=X.device)
zeros = torch.zeros((batch_size,), device=X.device)
trainer_D.zero_grad()
real_Y = net_D(X)
fake_X = net_G(Z)
# Do not need to compute gradient for `net_G`, detach it from
# computing gradients.
fake_Y = net_D(fake_X.detach())
loss_D = (loss(real_Y, ones.reshape(real_Y.shape)) +
loss(fake_Y, zeros.reshape(fake_Y.shape))) / 2
loss_D.backward()
trainer_D.step()
return loss_D
def update_D(X, Z, net_D, net_G, loss, trainer_D): 이 함수는 판별자 네트워크를 업데이트하는 역할을 합니다. 이 함수는 다음 매개변수들을 입력으로 받습니다.
X: 실제 데이터 샘플 배치
Z: 생성자 네트워크에 의해 생성된 가짜 데이터 샘플 배치
net_D: 판별자 네트워크
net_G: 생성자 네트워크
loss: 손실 함수
trainer_D: 판별자 네트워크를 최적화하기 위한 옵티마이저
batch_size = X.shape[0]: 배치 크기를 구합니다. 이는 입력 데이터 X의 첫 번째 차원인 배치 차원의 크기입니다.
ones = torch.ones((batch_size,), device=X.device): 길이가 batch_size인 1로 채워진 텐서를 생성합니다. 이 텐서는 실제 데이터에 대한 레이블로 사용됩니다. device 매개변수는 텐서를 어느 장치 (예: CPU 또는 GPU)에서 계산할 것인지를 지정합니다.
zeros = torch.zeros((batch_size,), device=X.device): 길이가 batch_size인 0으로 채워진 텐서를 생성합니다. 이 텐서는 가짜 데이터에 대한 레이블로 사용됩니다.
trainer_D.zero_grad(): 판별자 네트워크의 그래디언트를 초기화합니다. 이는 새로운 그래디언트를 계산하기 전에 이전 그래디언트를 제거하는데 사용됩니다.
real_Y = net_D(X): 실제 데이터 X를 판별자 네트워크에 전달하여 실제 데이터의 판별 결과를 계산합니다.
fake_X = net_G(Z): 생성자 네트워크에 의해 생성된 가짜 데이터 Z를 판별자 네트워크에 전달하여 가짜 데이터의 판별 결과를 계산합니다.
fake_Y = net_D(fake_X.detach()): 생성자 네트워크에 의해 생성된 가짜 데이터 fake_X를 판별자 네트워크에 전달합니다. .detach()를 사용하여 생성자 네트워크의 그래디언트를 계산하지 않도록 설정합니다.
loss_D = (loss(real_Y, ones.reshape(real_Y.shape)) + loss(fake_Y, zeros.reshape(fake_Y.shape))) / 2: 실제 데이터와 가짜 데이터에 대한 판별자의 손실을 계산합니다. 이 손실은 실제 데이터의 판별 결과와 1 사이의 손실, 그리고 가짜 데이터의 판별 결과와 0 사이의 손실을 평균화한 것입니다.
loss_D.backward(): 판별자 네트워크의 손실에 대한 그래디언트를 계산합니다.
trainer_D.step(): 판별자 네트워크의 매개변수를 업데이트합니다. 최적화된 그래디언트를 사용하여 신경망의 매개변수를 조정합니다.
return loss_D: 계산된 판별자의 손실을 반환합니다.
이 함수는 GAN의 판별자 네트워크를 학습하기 위해 사용되며, 생성자와 판별자 사이의 경쟁을 통해 모델을 훈련시키는 데 필요합니다.
The generator is updated similarly. Here we reuse the cross-entropy loss but change the label of the fake data from0to1.
생성기도 비슷하게 업데이트됩니다. 여기서는 교차 엔트로피 손실을 재사용하지만 가짜 데이터의 레이블을 0에서 1로 변경합니다.
#@save
def update_G(Z, net_D, net_G, loss, trainer_G):
"""Update generator."""
batch_size = Z.shape[0]
ones = torch.ones((batch_size,), device=Z.device)
trainer_G.zero_grad()
# We could reuse `fake_X` from `update_D` to save computation
fake_X = net_G(Z)
# Recomputing `fake_Y` is needed since `net_D` is changed
fake_Y = net_D(fake_X)
loss_G = loss(fake_Y, ones.reshape(fake_Y.shape))
loss_G.backward()
trainer_G.step()
return loss_G
def update_G(Z, net_D, net_G, loss, trainer_G): 이 함수는 생성자 네트워크를 업데이트하는 역할을 합니다. 이 함수는 다음 매개변수들을 입력으로 받습니다.
Z: 생성자 네트워크의 입력으로 사용될 무작위 노이즈 벡터 배치
net_D: 판별자 네트워크
net_G: 생성자 네트워크
loss: 손실 함수
trainer_G: 생성자 네트워크를 최적화하기 위한 옵티마이저
batch_size = Z.shape[0]: 배치 크기를 구합니다. 이는 입력 데이터 Z의 첫 번째 차원인 배치 차원의 크기입니다.
ones = torch.ones((batch_size,), device=Z.device): 길이가 batch_size인 1로 채워진 텐서를 생성합니다. 이 텐서는 생성자가 생성한 데이터에 대한 레이블로 사용됩니다. device 매개변수는 텐서를 어느 장치 (예: CPU 또는 GPU)에서 계산할 것인지를 지정합니다.
trainer_G.zero_grad(): 생성자 네트워크의 그래디언트를 초기화합니다. 이는 새로운 그래디언트를 계산하기 전에 이전 그래디언트를 제거하는데 사용됩니다.
fake_X = net_G(Z): 생성자 네트워크에 무작위 노이즈 Z를 전달하여 가짜 데이터를 생성합니다.
fake_Y = net_D(fake_X): 생성된 가짜 데이터 fake_X를 판별자 네트워크에 전달하여 가짜 데이터의 판별 결과를 계산합니다. 이 부분은 판별자를 통해 가짜 데이터를 판별한 결과입니다.
loss_G = loss(fake_Y, ones.reshape(fake_Y.shape)): 생성자의 손실을 계산합니다. 이 손실은 생성자가 생성한 가짜 데이터에 대한 판별자의 출력과 1 사이의 손실을 나타냅니다. 생성자는 판별자를 속이려고 노력하며, 따라서 이 손실을 최소화하려고 합니다.
loss_G.backward(): 생성자 네트워크의 손실에 대한 그래디언트를 계산합니다.
trainer_G.step(): 생성자 네트워크의 매개변수를 업데이트합니다. 최적화된 그래디언트를 사용하여 신경망의 매개변수를 조정합니다.
return loss_G: 계산된 생성자의 손실을 반환합니다.
이 함수는 GAN의 생성자 네트워크를 학습하기 위해 사용됩니다. 생성자는 판별자를 속이려고 하며, 이를 통해 실제와 유사한 데이터를 생성하도록 훈련됩니다.
Both the discriminator and the generator performs a binary logistic regression with the cross-entropy loss. We use Adam to smooth the training process. In each iteration, we first update the discriminator and then the generator. We visualize both losses and generated examples.
판별자와 생성자 모두 교차 엔트로피 손실을 사용하여 이진 로지스틱 회귀를 수행합니다. 우리는 훈련 과정을 원활하게 하기 위해 Adam을 사용합니다. 각 반복에서 먼저 판별자를 업데이트한 다음 생성자를 업데이트합니다. 손실과 생성된 사례를 모두 시각화합니다.
def train(net_D, net_G, data_iter, num_epochs, lr_D, lr_G, latent_dim, data):
loss = nn.BCEWithLogitsLoss(reduction='sum')
for w in net_D.parameters():
nn.init.normal_(w, 0, 0.02)
for w in net_G.parameters():
nn.init.normal_(w, 0, 0.02)
trainer_D = torch.optim.Adam(net_D.parameters(), lr=lr_D)
trainer_G = torch.optim.Adam(net_G.parameters(), lr=lr_G)
animator = d2l.Animator(xlabel='epoch', ylabel='loss',
xlim=[1, num_epochs], nrows=2, figsize=(5, 5),
legend=['discriminator', 'generator'])
animator.fig.subplots_adjust(hspace=0.3)
for epoch in range(num_epochs):
# Train one epoch
timer = d2l.Timer()
metric = d2l.Accumulator(3) # loss_D, loss_G, num_examples
for (X,) in data_iter:
batch_size = X.shape[0]
Z = torch.normal(0, 1, size=(batch_size, latent_dim))
metric.add(update_D(X, Z, net_D, net_G, loss, trainer_D),
update_G(Z, net_D, net_G, loss, trainer_G),
batch_size)
# Visualize generated examples
Z = torch.normal(0, 1, size=(100, latent_dim))
fake_X = net_G(Z).detach().numpy()
animator.axes[1].cla()
animator.axes[1].scatter(data[:, 0], data[:, 1])
animator.axes[1].scatter(fake_X[:, 0], fake_X[:, 1])
animator.axes[1].legend(['real', 'generated'])
# Show the losses
loss_D, loss_G = metric[0]/metric[2], metric[1]/metric[2]
animator.add(epoch + 1, (loss_D, loss_G))
print(f'loss_D {loss_D:.3f}, loss_G {loss_G:.3f}, '
f'{metric[2] / timer.stop():.1f} examples/sec')
이 함수는 GAN 모델을 훈련하는 주요 루프를 포함하고 있습니다. 주요 단계는 다음과 같습니다.
BCEWithLogitsLoss를 사용하여 손실 함수를 설정합니다. 이 손실 함수는 이진 분류 손실 함수로 사용됩니다.
판별자와 생성자 네트워크의 가중치를 초기화합니다. 일반적으로 작은 랜덤값으로 초기화합니다.
Adam 옵티마이저를 설정하여 판별자와 생성자 네트워크의 매개변수를 최적화합니다.
애니메이터를 설정하여 훈련 중에 손실과 생성된 데이터를 시각화합니다.
주어진 에포크 수(num_epochs) 동안 훈련 루프를 실행합니다. 각 에포크에서는 판별자와 생성자 네트워크를 업데이트하고 손실을 누적합니다.
생성된 예제를 시각화하여 실제 데이터와 비교합니다.
각 에포크의 손실을 기록하고 애니메이터를 통해 시각화합니다.
훈련이 끝난 후 최종 손실과 훈련 속도를 출력합니다.
이 함수를 호출하여 GAN 모델을 훈련하고 결과를 시각화할 수 있습니다.
Now we specify the hyperparameters to fit the Gaussian distribution.
lr_D, lr_G, latent_dim, num_epochs: 이 코드 라인에서는 GAN 모델을 훈련하는데 사용되는 하이퍼파라미터를 설정합니다.
lr_D: 판별자 네트워크를 최적화하는 데 사용되는 학습률입니다.
lr_G: 생성자 네트워크를 최적화하는 데 사용되는 학습률입니다.
latent_dim: 생성자의 입력 노이즈 벡터의 차원입니다.
num_epochs: 훈련하는 데 사용할 에포크(훈련 주기) 수입니다.
train(net_D, net_G, data_iter, num_epochs, lr_D, lr_G, latent_dim, data[:100].detach().numpy()): 이 코드 라인에서는 train 함수를 호출하여 GAN 모델을 실제로 훈련합니다.
net_D: 판별자 네트워크
net_G: 생성자 네트워크
data_iter: 데이터 반복자
num_epochs: 설정한 에포크 수
lr_D, lr_G: 판별자와 생성자의 학습률
latent_dim: 생성자의 입력 노이즈 벡터의 차원
data[:100].detach().numpy(): 사용할 데이터 중에서 처음 100개의 데이터를 선택하고 넘파이 배열로 변환한 것입니다. GAN 모델을 훈련할 때는 실제 데이터의 일부만 사용하여 훈련합니다.
이 코드를 실행하면 GAN 모델이 주어진 데이터에 대해 훈련되고, 훈련 과정 중에 손실이 감소하면서 생성된 가짜 데이터가 실제 데이터와 유사해지는 것을 관찰할 수 있습니다.
loss_D 0.693, loss_G 0.693, 1020.0 examples/sec
20.1.5.Summary
Generative adversarial networks (GANs) composes of two deep networks, the generator and the discriminator.
GAN(Generative Adversarial Network)은 생성자와 판별자라는 두 개의 심층 네트워크로 구성됩니다.
The generator generates the image as much closer to the true image as possible to fool the discriminator, via maximizing the cross-entropy loss,i.e.,maxlog(D(x′)).
생성기는 교차 엔트로피 손실(예: maxlog(D(x′)))을 최대화하여 판별기를 속이기 위해 가능한 한 실제 이미지에 더 가까운 이미지를 생성합니다.
The discriminator tries to distinguish the generated images from the true images, via minimizing the cross-entropy loss,i.e.,min−y logD(x)−(1−y)log(1−D(x)).
판별자는 교차 엔트로피 손실, 즉 min−y logD(x)−(1−y)log(1−D(x))를 최소화하여 생성된 이미지를 실제 이미지와 구별하려고 시도합니다.
20.1.6.Exercises
Does an equilibrium exist where the generator wins,i.e.the discriminator ends up unable to distinguish the two distributions on finite samples?
생성자가 승리하는 평형이 존재합니까? 즉, 판별자가 유한 샘플에서 두 분포를 구별할 수 없게 됩니까?
As we have seen inSection 19.3, we can accelerate HPO by distributing the evaluation of hyperparameter configurations across either multiple instances or multiples CPUs / GPUs on a single instance. However, compared to random search, it is not straightforward to run successive halving (SH) asynchronously in a distributed setting. Before we can decide which configuration to run next, we first have to collect all observations at the current rung level. This requires to synchronize workers at each rung level. For example, for the lowest rung levelr min, we first have to evaluate allN=η**kconfigurations, before we can promote the1/ηof them to the next rung level.
섹션 19.3에서 살펴본 것처럼 하이퍼파라미터 구성 평가를 여러 인스턴스 또는 단일 인스턴스의 여러 CPU/GPU에 분산하여 HPO를 가속화할 수 있습니다. 그러나 무작위 검색에 비해 분산 설정에서 비동기적으로 연속 반감기(SH)를 실행하는 것은 간단하지 않습니다. 다음에 실행할 구성을 결정하기 전에 먼저 현재 단계 수준에서 모든 관찰을 수집해야 합니다. 이를 위해서는 각 단계 수준에서 작업자를 동기화해야 합니다. 예를 들어, 가장 낮은 단계 수준 r min의 경우 먼저 모든 N=eta**k 구성을 평가해야 그 중 1/eta를 다음 단계 수준으로 승격할 수 있습니다.
In any distributed system, synchronization typically implies idle time for workers. First, we often observe high variations in training time across hyperparameter configurations. For example, assuming the number of filters per layer is a hyperparameter, then networks with less filters finish training faster than networks with more filters, which implies idle worker time due to stragglers. Moreover, the number of slots in a rung level is not always a multiple of the number of workers, in which case some workers may even sit idle for a full batch.
모든 분산 시스템에서 동기화는 일반적으로 작업자의 유휴 시간을 의미합니다. 첫째, 우리는 하이퍼파라미터 구성 전반에 걸쳐 훈련 시간의 높은 변동을 자주 관찰합니다. 예를 들어, 레이어당 필터 수가 하이퍼파라미터라고 가정하면 필터가 적은 네트워크는 필터가 많은 네트워크보다 훈련을 더 빨리 완료합니다. 이는 낙오자로 인한 유휴 작업자 시간을 의미합니다. 또한 단계 수준의 슬롯 수가 항상 작업자 수의 배수가 되는 것은 아니며, 이 경우 일부 작업자는 전체 배치 동안 유휴 상태로 있을 수도 있습니다.
FigureFig. 19.5.1shows the scheduling of synchronous SH withη=2for four different trials with two workers. We start with evaluating Trial-0 and Trial-1 for one epoch and immediately continue with the next two trials once they are finished. We first have to wait until Trial-2 finishes, which takes substantially more time than the other trials, before we can promote the best two trials, i.e., Trial-0 and Trial-3 to the next rung level. This causes idle time for Worker-1. Then, we continue with Rung 1. Also, here Trial-3 takes longer than Trial-0, which leads to an additional ideling time of Worker-0. Once, we reach Rung-2, only the best trial, Trial-0, remains which occupies only one worker. To avoid that Worker-1 idles during that time, most implementaitons of SH continue already with the next round, and start evaluating new trials (e.g Trial-4) on the first rung.
그림 그림 19.5.1은 2명의 워커를 사용한 4가지 다른 시도에 대해 θ=2인 동기식 SH의 스케줄링을 보여줍니다. 한 에포크 동안 Trial-0과 Trial-1을 평가하는 것으로 시작하고, 완료되면 다음 두 번의 시도를 즉시 계속합니다. 가장 좋은 두 가지 시도, 즉 Trial-0과 Trial-3을 다음 단계로 승격하려면 먼저 다른 시도보다 훨씬 더 많은 시간이 걸리는 Trial-2가 완료될 때까지 기다려야 합니다. 이로 인해 Worker-1의 유휴 시간이 발생합니다. 그런 다음 Rung 1을 계속 진행합니다. 또한 여기서 Trial-3은 Trial-0보다 시간이 오래 걸리므로 Worker-0의 추가 유휴 시간이 발생합니다. 일단 Rung-2에 도달하면 가장 좋은 시도인 Trial-0만 남고 작업자는 한 명만 차지합니다. 해당 시간 동안 Worker-1이 유휴 상태가 되는 것을 방지하기 위해 대부분의 SH 구현은 이미 다음 라운드에서 계속되고 첫 번째 단계에서 새로운 시도(예: Trial-4) 평가를 시작합니다.
Asynchronous successive halving (ASHA)(Liet al., 2018)adapts SH to the asynchronous parallel scenario. The main idea of ASHA is to promote configurations to the next rung level as soon as we collected at leastηobservations on the current rung level. This decision rule may lead to suboptimal promotions: configurations can be promoted to the next rung level, which in hindsight do not compare favourably against most others at the same rung level. On the other hand, we get rid of all synchronization points this way. In practice, such suboptimal initial promotions have only a modest impact on performance, not only because the ranking of hyperparameter configurations is often fairly consistent across rung levels, but also because rungs grow over time and reflect the distribution of metric values at this level better and better. If a worker is free, but no configuration can be promoted, we start a new configuration withr = r min, i.e the first rung level.
비동기 연속 반감기(ASHA)(Li et al., 2018)는 SH를 비동기 병렬 시나리오에 적용합니다. ASHA의 주요 아이디어는 현재 단계 수준에서 최소 eta 관측치를 수집하자마자 구성을 다음 단계 수준으로 승격시키는 것입니다. 이 결정 규칙은 최적이 아닌 승격으로 이어질 수 있습니다. 구성은 다음 단계 수준으로 승격될 수 있으며, 돌이켜보면 동일한 단계 수준의 대부분의 다른 단계와 비교했을 때 호의적이지 않습니다. 반면에 우리는 이런 방식으로 모든 동기화 지점을 제거합니다. 실제로 이러한 최적이 아닌 초기 승격은 성능에 미미한 영향만 미칠 뿐입니다. 이는 초매개변수 구성의 순위가 단계 수준 전체에서 상당히 일관되는 경우가 많을 뿐만 아니라 단계가 시간이 지남에 따라 증가하고 이 수준에서 메트릭 값의 분포를 더 잘 반영하고 더 잘 반영하기 때문입니다. 더 나은. 작업자가 무료이지만 승격할 수 있는 구성이 없으면 r = r min으로 새 구성, 즉 첫 번째 단계 수준을 시작합니다.
Fig. 19.5.2shows the scheduling of the same configurations for ASHA. Once Trial-1 finishes, we collect the results of two trials (i.e Trial-0 and Trial-1) and immediately promote the better of them (Trial-0) to the next rung level. After Trial-0 finishes on rung 1, there are too few trials there in order to support a further promotion. Hence, we continue with rung 0 and evaluate Trial-3. Once Trial-3 finishes, Trial-2 is still pending. At this point we have 3 trials evaluated on rung 0 and one trial evaluated already on rung 1. Since Trial-3 performs worse than Trial-0 at rung 0, andη = 2, we cannot promote any new trial yet, and Worker-1 starts Trial-4 from scratch instead. However, once Trial-2 finishes and scores worse than Trial-3, the latter is promoted towards rung 1. Afterwards, we collected 2 evaluations on rung 1, which means we can now promote Trial-0 towards rung 2. At the same time, Worker-1 continues with evaluating new trials (i.e., Trial-5) on rung 0.
그림 19.5.2는 ASHA에 대한 동일한 구성의 스케줄링을 보여줍니다. Trial-1이 완료되면 두 가지 시도(즉, Trial-0 및 Trial-1)의 결과를 수집하고 그 중 더 나은 것(Trial-0)을 즉시 다음 단계 수준으로 승격합니다. 평가판 0이 단계 1에서 끝난 후에는 추가 승격을 지원하기에는 시도 횟수가 너무 적습니다. 따라서 우리는 단계 0을 계속 진행하고 평가판 3을 평가합니다. 평가판 3이 끝나면 평가판 2가 계속 보류됩니다. 이 시점에서 우리는 단계 0에서 평가된 3개의 시도와 단계 1에서 이미 평가된 하나의 시도를 가지고 있습니다. Trial-3은 단계 0에서 Trial-0보다 성능이 떨어지고 θ = 2이므로 아직 새로운 시도를 승격할 수 없으며 Worker- 1은 대신 Trial-4를 처음부터 시작합니다. 그러나 평가판 2가 완료되고 평가판 3보다 낮은 점수를 받으면 후자는 단계 1로 승격됩니다. 이후 단계 1에서 2개의 평가를 수집했습니다. 이는 이제 평가판 0을 단계 2로 승격할 수 있음을 의미합니다. , 작업자-1은 단계 0에서 새로운 시도(즉, 시도 5)를 계속 평가합니다.
import logging
from d2l import torch as d2l
logging.basicConfig(level=logging.INFO)
import matplotlib.pyplot as plt
from syne_tune import StoppingCriterion, Tuner
from syne_tune.backend.python_backend import PythonBackend
from syne_tune.config_space import loguniform, randint
from syne_tune.experiments import load_experiment
from syne_tune.optimizer.baselines import ASHA
위의 코드는 HPO(Hyperparameter Optimization) 실험을 수행하기 위한 설정을 위한 코드입니다. 주요 라이브러리와 로깅 설정을 포함하고 있습니다.
import logging: 로깅(logging)을 위한 파이썬 라이브러리를 가져옵니다.
from d2l import torch as d2l: "d2l" 라이브러리에서 "torch" 모듈을 가져옵니다. 이 모듈은 PyTorch 기반의 딥 러닝 코드 작성을 지원합니다.
logging.basicConfig(level=logging.INFO): 로깅 레벨을 INFO로 설정하고 기본 로깅 구성을 초기화합니다. 이를 통해 코드 실행 중에 로그 메시지를 출력할 수 있습니다.
import matplotlib.pyplot as plt: Matplotlib을 사용하여 그래프를 그리기 위한 모듈을 가져옵니다.
from syne_tune import StoppingCriterion, Tuner: SyneTune 라이브러리에서 StoppingCriterion과 Tuner 클래스를 가져옵니다. 이 클래스들은 HPO 실험을 관리하고 조정하는 데 사용됩니다.
from syne_tune.backend.python_backend import PythonBackend: SyneTune에서 사용하는 백엔드(backend) 중 하나인 PythonBackend를 가져옵니다. 백엔드는 HPO 실험을 실행하는 방식을 지정합니다.
from syne_tune.config_space import loguniform, randint: HPO 실험에서 사용할 하이퍼파라미터 공간을 정의하기 위해 loguniform과 randint 등의 함수를 가져옵니다. 이 함수들을 사용하여 하이퍼파라미터를 샘플링할 수 있습니다.
from syne_tune.experiments import load_experiment: SyneTune에서 실험을 로드하고 관리하기 위한 함수를 가져옵니다.
from syne_tune.optimizer.baselines import ASHA: ASHA(Hyperband 기반의 비동기 하이퍼파라미터 최적화 알고리즘)를 가져옵니다. ASHA는 하이퍼파라미터 최적화에 사용되는 알고리즘 중 하나입니다.
INFO:root:SageMakerBackend is not imported since dependencies are missing. You can install them with
pip install 'syne-tune[extra]'
AWS dependencies are not imported since dependencies are missing. You can install them with
pip install 'syne-tune[aws]'
or (for everything)
pip install 'syne-tune[extra]'
AWS dependencies are not imported since dependencies are missing. You can install them with
pip install 'syne-tune[aws]'
or (for everything)
pip install 'syne-tune[extra]'
INFO:root:Ray Tune schedulers and searchers are not imported since dependencies are missing. You can install them with
pip install 'syne-tune[raytune]'
or (for everything)
pip install 'syne-tune[extra]'
19.5.1.Objective Function
We will useSyne Tunewith the same objective function as inSection 19.3.
섹션 19.3과 동일한 목적 함수를 사용하여 Syne Tune을 사용하겠습니다.
def hpo_objective_lenet_synetune(learning_rate, batch_size, max_epochs):
from syne_tune import Reporter
from d2l import torch as d2l
model = d2l.LeNet(lr=learning_rate, num_classes=10)
trainer = d2l.HPOTrainer(max_epochs=1, num_gpus=1)
data = d2l.FashionMNIST(batch_size=batch_size)
model.apply_init([next(iter(data.get_dataloader(True)))[0]], d2l.init_cnn)
report = Reporter()
for epoch in range(1, max_epochs + 1):
if epoch == 1:
# Initialize the state of Trainer
trainer.fit(model=model, data=data)
else:
trainer.fit_epoch()
validation_error = trainer.validation_error().cpu().detach().numpy()
report(epoch=epoch, validation_error=float(validation_error))
위의 코드는 SyneTune 라이브러리를 사용하여 하이퍼파라미터 최적화를 수행하는 목적 함수(hpo_objective_lenet_synetune)를 정의한 부분입니다. 이 함수는 LeNet 아키텍처를 사용하여 이미지 분류 모델을 훈련하고, 각 하이퍼파라미터 설정에 대한 검증 오차(validation error)를 반환합니다.
주요 내용은 다음과 같습니다.
learning_rate, batch_size, max_epochs 등의 하이퍼파라미터를 입력으로 받습니다.
model = d2l.LeNet(lr=learning_rate, num_classes=10): 주어진 학습률(learning_rate)과 클래스 수(num_classes)를 가지고 LeNet 모델을 생성합니다.
trainer = d2l.HPOTrainer(max_epochs=1, num_gpus=1): 하이퍼파라미터 최적화를 위한 트레이너를 생성합니다. max_epochs는 1로 설정되어 있으므로 하나의 에포크만 훈련됩니다.
data = d2l.FashionMNIST(batch_size=batch_size): Fashion MNIST 데이터셋을 로드하고 주어진 배치 크기(batch_size)로 데이터를 미니배치 형태로 제공합니다.
model.apply_init([next(iter(data.get_dataloader(True)))[0]], d2l.init_cnn): 초기화 함수 d2l.init_cnn을 사용하여 모델을 초기화합니다.
report = Reporter(): 실험 결과를 기록하기 위한 Reporter 객체를 생성합니다.
반복문을 통해 에포크(epoch)를 1부터 max_epochs까지 증가시키면서 모델을 훈련합니다.
에포크가 1인 경우에는 트레이너를 초기화하고 모델을 훈련시킵니다.
에포크가 1보다 큰 경우에는 trainer.fit_epoch()를 호출하여 한 번의 에포크를 추가로 훈련시킵니다.
trainer.validation_error().cpu().detach().numpy()를 통해 검증 오차(validation error)를 계산하고 반환합니다.
report(epoch=epoch, validation_error=float(validation_error))를 사용하여 현재 에포크와 검증 오차를 Reporter에 기록합니다.
즉, 이 함수는 주어진 하이퍼파라미터 설정으로 모델을 훈련하고 검증 오차를 반환하는 역할을 합니다. SyneTune은 이 함수를 사용하여 다양한 하이퍼파라미터 설정을 시도하고 최적의 설정을 찾습니다.
We will also use the same configuration space as before:
위의 코드는 SyneTune 라이브러리를 사용하여 하이퍼파라미터 최적화를 수행할 때 사용되는 설정과 초기 하이퍼파라미터 값을 정의하는 부분입니다. 주요 내용은 다음과 같습니다.
min_number_of_epochs: 실험에서 허용하는 최소 에포크 수입니다. 이 값은 2로 설정되어 있습니다.
max_number_of_epochs: 실험에서 허용하는 최대 에포크 수입니다. 이 값은 10으로 설정되어 있습니다.
eta: Successive Halving 알고리즘에서 사용되는 파라미터로, 곱셈 연산을 수행할 때 사용됩니다. 이 값은 2로 설정되어 있습니다.
config_space: 하이퍼파라미터 공간을 정의하는 부분입니다. 여기서는 세 가지 하이퍼파라미터인 learning_rate, batch_size, max_epochs의 범위를 지정합니다.
learning_rate: 로그균등 분포(loguniform distribution)를 사용하여 1e-2에서 1 사이의 값으로 설정됩니다.
batch_size: 균등 분포(uniform distribution)를 사용하여 32에서 256 사이의 정수 값으로 설정됩니다.
max_epochs: max_number_of_epochs로 설정된 최대 에포크 값을 가집니다.
initial_config: 초기 하이퍼파라미터 설정을 정의하는 부분입니다. 여기서는 learning_rate를 0.1로, batch_size를 128로 초기화합니다.
이러한 설정과 초기값은 하이퍼파라미터 최적화 실험을 수행할 때 사용됩니다. SyneTune 라이브러리를 통해 하이퍼파라미터 탐색을 진행하면서 이러한 설정 범위 내에서 하이퍼파라미터 값을 조정하고 최적의 설정을 찾게 됩니다.
19.5.2.Asynchronous Scheduler
First, we define the number of workers that evaluate trials concurrently. We also need to specify how long we want to run random search, by defining an upper limit on the total wall-clock time.
먼저, 동시에 시험을 평가하는 작업자 수를 정의합니다. 또한 총 벽시계 시간의 상한을 정의하여 무작위 검색을 실행할 기간을 지정해야 합니다.
n_workers = 2 # Needs to be <= the number of available GPUs
max_wallclock_time = 12 * 60 # 12 minutes
위의 코드는 하이퍼파라미터 최적화 실험을 수행할 때 사용되는 두 가지 중요한 설정을 나타냅니다.
n_workers: 실험 도중에 병렬로 실행되는 워커(작업자)의 수를 나타냅니다. 이 수는 사용 가능한 GPU 수보다 작거나 같아야 합니다. 여기서는 2로 설정되어 있으므로 최대 2개의 GPU 또는 병렬 작업자를 사용할 수 있음을 의미합니다.
max_wallclock_time: 하이퍼파라미터 최적화 실험의 최대 시간을 분 단위로 나타냅니다. 이 값은 12 * 60으로 설정되어 있으므로 최대 12시간(720분) 동안 실험을 진행할 수 있음을 의미합니다. 실험 시간이 이 설정 값 이내에 끝나도록 실험을 조절합니다.
The code for running ASHA is a simple variation of what we did for asynchronous random search.
ASHA를 실행하기 위한 코드는 비동기 무작위 검색을 위해 수행한 작업의 간단한 변형입니다.
위의 코드는 하이퍼파라미터 최적화 실험에서 사용되는 스케줄러인 ASHA (Asynchronous Successive Halving Algorithm)를 설정하는 부분입니다.
mode: ASHA 알고리즘에서 최적화할 메트릭의 모드를 나타냅니다. "min"으로 설정되어 있으므로 이 알고리즘은 가장 낮은 값을 찾는 데 초점을 맞추게 됩니다.
metric: ASHA 알고리즘에서 최적화할 메트릭의 이름을 나타냅니다. 이 경우 "validation_error"로 설정되어 있으므로 검증 오차(validation error)를 최소화하려고 시도합니다.
resource_attr: 실험에서 사용할 리소스 속성을 나타냅니다. 여기서는 "epoch"으로 설정되어 있으므로 에포크(epoch) 수를 리소스로 사용하여 하이퍼파라미터 최적화를 수행합니다.
max_resource_attr: ASHA 알고리즘에서 사용할 최대 리소스 속성을 지정합니다. 이 경우 "max_epochs"로 설정되어 있으므로 최대 에포크 수가 사용됩니다.
grace_period: ASHA 알고리즘에서 고려할 하이퍼파라미터를 선택하는데 필요한 최소 리소스 수를 나타냅니다. 이 값은 "min_number_of_epochs"로 설정되어 있으므로 최소 에포크 수만큼 리소스가 할당된 경우에만 하이퍼파라미터가 선택됩니다.
reduction_factor: ASHA 알고리즘에서 에포크 수를 줄이는 비율을 나타냅니다. 이 값은 "eta"로 설정되어 있으므로 2입니다. 이것은 각 라운드에서 절반씩 에포크 수를 줄이는 것을 의미합니다.
ASHA 스케줄러는 하이퍼파라미터 최적화를 수행하는 데 사용되며, 리소스 속성을 기반으로 하이퍼파라미터 검색을 조절하는 데 도움을 줍니다.
INFO:syne_tune.optimizer.schedulers.fifo:max_resource_level = 10, as inferred from config_space
INFO:syne_tune.optimizer.schedulers.fifo:Master random_seed = 3140976097
Here,metricandresource_attrspecify the key names used with thereportcallback, andmax_resource_attrdenotes which input to the objective function corresponds tor max. Moreover,grace_periodprovidesr min, andreduction_factorisη. We can run Syne Tune as before (this will take about 12 minutes):
여기에서 metric 및 resources_attr은 보고서 콜백과 함께 사용되는 키 이름을 지정하고 max_resource_attr은 목적 함수에 대한 입력이 r max에 해당하는지 나타냅니다. 또한, Grace_기간은 r min을 제공하고, 감소_인자는 θ입니다. 이전과 같이 Syne Tune을 실행할 수 있습니다(약 12분 소요).
위의 코드는 ASHA 스케줄러를 사용하여 하이퍼파라미터 최적화 실험을 실행하는 부분입니다.
trial_backend: 실험을 실행하는 백엔드(Backend)를 설정합니다. 여기서는 PythonBackend를 사용하며, tune_function에는 hpo_objective_lenet_synetune 함수를, config_space에는 하이퍼파라미터 검색 공간을 설정합니다. 이 백엔드는 Python 함수를 호출하여 실험을 실행합니다.
stop_criterion: ASHA 스케줄러를 중지시키는 기준을 설정합니다. max_wallclock_time은 실험을 실행할 최대 시간(분)을 설정하며, 이 값은 12 * 60으로 설정되어 있으므로 12분 동안 실험을 실행한 후 중지됩니다.
tuner: Tuner 클래스를 사용하여 최적화 프로세스를 설정합니다. trial_backend에는 백엔드 설정, scheduler에는 ASHA 스케줄러, stop_criterion에는 중지 기준, n_workers에는 사용할 워커(실험 실행 프로세스) 수를 설정합니다. print_update_interval은 중간 업데이트를 출력하는 간격을 설정하며, max_wallclock_time의 60%에 해당하는 값으로 설정됩니다.
tuner.run(): 이 명령은 하이퍼파라미터 최적화 실험을 실행합니다. ASHA 스케줄러를 사용하여 여러 하이퍼파라미터 조합을 평가하고 최적의 하이퍼파라미터를 찾습니다. 실험이 실행되는 동안 중간 업데이트가 출력됩니다.
Note that we are running a variant of ASHA where underperforming trials are stopped early. This is different to our implementation inSection 19.4.1, where each training job is started with a fixedmax_epochs. In the latter case, a well-performing trial which reaches the full 10 epochs, first needs to train 1, then 2, then 4, then 8 epochs, each time starting from scratch. This type of pause-and-resume scheduling can be implemented efficiently by checkpointing the training state after each epoch, but we avoid this extra complexity here. After the experiment has finished, we can retrieve and plot results.
우리는 실적이 저조한 시험을 조기에 중단하는 ASHA 변형을 실행하고 있습니다. 이는 각 훈련 작업이 고정된 max_epochs로 시작되는 섹션 19.4.1의 구현과 다릅니다. 후자의 경우 전체 10개 에포크에 도달하는 잘 수행되는 시험은 처음부터 처음부터 시작할 때마다 먼저 1개, 2개, 4개, 8개 에포크를 훈련해야 합니다. 이러한 유형의 일시 중지 및 재개 스케줄링은 각 에포크 이후 훈련 상태를 검사하여 효율적으로 구현할 수 있지만 여기서는 이러한 추가적인 복잡성을 피합니다. 실험이 완료된 후 결과를 검색하고 플롯할 수 있습니다.
d2l.set_figsize()
e = load_experiment(tuner.name)
e.plot()
위의 코드는 실험 결과를 시각화하는 부분입니다.
d2l.set_figsize(): 그래프의 크기를 설정하는 함수입니다. 이 경우 그래프의 크기를 조정합니다.
e = load_experiment(tuner.name): load_experiment 함수를 사용하여 이전에 실행한 실험 결과를 로드합니다. tuner.name은 이전에 실행한 튜너의 이름을 나타냅니다.
e.plot(): 로드한 실험 결과를 시각화합니다. 이로써 실험 결과 그래프가 표시됩니다. 실험 결과에는 하이퍼파라미터 값에 대한 메트릭(metric)의 변화 추이와 관련된 정보가 포함됩니다.
WARNING:matplotlib.legend:No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
19.5.3.Visualize the Optimization Process
Once more, we visualize the learning curves of every trial (each color in the plot represents a trial). Compare this to asynchronous random search inSection 19.3. As we have seen for successive halving inSection 19.4, most of the trials are stopped at 1 or 2 epochs (r minorη ∗ r min). However, trials do not stop at the same point, because they require different amount of time per epoch. If we ran standard successive halving instead of ASHA, we would need to synchronize our workers, before we can promote configurations to the next rung level.
다시 한 번 모든 시행의 학습 곡선을 시각화합니다(플롯의 각 색상은 시행을 나타냄). 이것을 섹션 19.3의 비동기 무작위 검색과 비교하십시오. 섹션 19.4에서 연속적인 반감기에 대해 살펴본 것처럼 대부분의 시행은 1 또는 2 에포크(r min 또는 θ * r min)에서 중단됩니다. 그러나 시도는 에포크마다 필요한 시간이 다르기 때문에 동일한 지점에서 멈추지 않습니다. ASHA 대신 표준 연속 절반을 실행한 경우 구성을 다음 단계 수준으로 승격하려면 먼저 작업자를 동기화해야 합니다.
d2l.set_figsize([6, 2.5]): 그래프의 크기를 설정하는 함수입니다. 이 경우 그래프의 가로 폭을 6로, 세로 높이를 2.5로 설정합니다.
results = e.results: 로드한 실험 결과에서 실제 결과 데이터를 가져옵니다.
for trial_id in results.trial_id.unique():: 실험 결과 중에서 고유한(trial_id가 다른) 각 실험에 대해서 반복합니다.
df = results[results["trial_id"] == trial_id]: 현재 반복 중인 trial_id에 해당하는 실험 결과 데이터를 선택합니다.
d2l.plt.plot(...): 선택한 실험 결과 데이터를 그래프로 표시합니다. x 축은 wall-clock time(실행 시간)을, y 축은 objective function(목적 함수) 값을 나타냅니다. marker="o"는 데이터 포인트를 원 형태로 표시하라는 옵션입니다.
d2l.plt.xlabel("wall-clock time"): x 축에 "wall-clock time" 레이블을 추가합니다.
d2l.plt.ylabel("objective function"): y 축에 "objective function" 레이블을 추가합니다.
이 코드는 실험 중 각 trial의 wall-clock time에 따른 objective function 값의 변화를 그래프로 표시하여 실험 결과를 시각적으로 확인할 수 있도록 합니다.
Text(0, 0.5, 'objective function')
19.5.4.Summary
Compared to random search, successive halving is not quite as trivial to run in an asynchronous distributed setting. To avoid synchronisation points, we promote configurations as quickly as possible to the next rung level, even if this means promoting some wrong ones. In practice, this usually does not hurt much, and the gains of asynchronous versus synchronous scheduling are usually much higher than the loss of the suboptimal decision making.
무작위 검색과 비교할 때, 연속적인 반감기는 비동기 분산 설정에서 실행하는 것이 그리 간단하지 않습니다. 동기화 지점을 피하기 위해 우리는 구성을 가능한 한 빨리 다음 단계 수준으로 승격합니다. 이는 잘못된 구성의 승격을 의미하더라도 마찬가지입니다. 실제로 이것은 일반적으로 큰 해를 끼치지 않으며 비동기식 스케줄링과 동기식 스케줄링의 이점은 일반적으로 차선책 의사 결정의 손실보다 훨씬 높습니다.
Asynchronous Successive Halving에 대해서 (GPT 3.5. 의 설명)
**비동기식 연속 하프링(Asynchronous Successive Halving)**은 하이퍼파라미터 최적화를 위한 고급 알고리즘 중 하나입니다. 이 방법은 Successive Halving 기술을 기반으로 하지만 병렬 처리와 비동기적 실행을 허용하여 계산 리소스를 효율적으로 활용합니다.
연속 하프링(Continuous Successive Halving) 또는 ASHA로도 알려진 이 방법은 다음과 같은 주요 아이디어를 포함합니다:
파라미터 세트는 다양한 하이퍼파라미터 구성을 나타냅니다. 초기에는 여러 구성이 생성됩니다.
리소스 할당: 초기에는 모든 파라미터 세트가 작은 리소스를 할당받아 빠르게 평가됩니다. 그런 다음 상위 성능을 보이는 세트가 더 많은 리소스를 할당받게 됩니다.
제거 및 확장: 하위 퍼포먼스를 보이는 파라미터 세트는 제거되고, 상위 퍼포먼스를 보이는 세트는 추가 리소스를 받아 성능을 더욱 정확하게 평가합니다. 이 과정을 반복하여 가장 우수한 하이퍼파라미터 세트를 찾습니다.
병렬 처리: ASHA는 파라미터 세트를 병렬로 처리하여 리소스 효율성을 극대화합니다. 이것은 다수의 하이퍼파라미터 구성을 동시에 평가하는 데 사용되며, 여러 프로세스 또는 워커(worker)가 동시에 실행됩니다.
비동기식 연속 하프링은 병렬 처리를 통해 하이퍼파라미터 최적화의 속도를 크게 높이며, 최적의 하이퍼파라미터 설정을 더욱 효율적으로 찾을 수 있도록 돕습니다. 이것은 계산 리소스를 최대한 활용하면서도 최상의 결과를 달성하기 위한 강력한 도구 중 하나입니다.
Training neural networks can be expensive even on moderate size datasets. Depending on the configuration space (Section 19.1.1.2), hyperparameter optimization requires tens to hundreds of function evaluations to find a well-performing hyperparameter configuration. As we have seen inSection 19.3, we can significantly speed up the overall wall-clock time of HPO by exploiting parallel resources, but this does not reduce the total amount of compute required.
적당한 크기의 데이터 세트에서도 신경망을 훈련하는 데 비용이 많이 들 수 있습니다. 구성 공간(19.1.1.2절)에 따라 하이퍼파라미터 최적화에는 성능이 좋은 하이퍼파라미터 구성을 찾기 위해 수십에서 수백 번의 함수 평가가 필요합니다. 섹션 19.3에서 살펴본 것처럼 병렬 리소스를 활용하여 HPO의 전체 벽시계 시간을 크게 단축할 수 있지만 이것이 필요한 총 컴퓨팅 양을 줄이지는 않습니다.
In this section, we will show how the evaluation of hyperparameter configurations can be sped up. Methods such as random search allocate the same amount of resources (e.g., number of epochs, training data points) to each hyperparameter evaluation.Fig. 19.4.1depicts learning curves of a set of neural networks trained with different hyperparameter configurations. After a few epochs we are already able to visually distinguish between well-performing and suboptimal configurations. However, the learning curves are noisy, and we might still require the full amount of 100 epochs to identify the best performing one.
이 섹션에서는 하이퍼파라미터 구성의 평가 속도를 높이는 방법을 보여줍니다. 무작위 검색과 같은 방법은 각 하이퍼파라미터 평가에 동일한 양의 리소스(예: 시대 수, 교육 데이터 포인트)를 할당합니다. 그림 19.4.1은 다양한 하이퍼파라미터 구성으로 훈련된 신경망 세트의 학습 곡선을 보여줍니다. 몇 번의 시대가 지나면 이미 성능이 좋은 구성과 최적이 아닌 구성을 시각적으로 구분할 수 있습니다. 그러나 학습 곡선에는 잡음이 많으므로 최고의 성과를 내는 것을 식별하려면 여전히 100개의 에포크가 필요할 수 있습니다.
Multi-fidelity hyperparameter optimization allocates more resources to promising configurations and stop evaluations of poorly performing ones early. This speeds up the optimization process, since we can try a larger number of configurations for the same total amount of resources.
다중 충실도 하이퍼파라미터 최적화는 유망한 구성에 더 많은 리소스를 할당하고 성능이 낮은 구성에 대한 평가를 조기에 중지합니다. 동일한 총 리소스 양에 대해 더 많은 수의 구성을 시도할 수 있으므로 최적화 프로세스 속도가 빨라집니다.
More formally, we expand our definition inSection 19.1.1, such that our objective functionf(x,r)gets an additional inputr∈[r min,r max], specifying the amount of resources that we are willing to spend for the evaluation of configurationx. We assume that the errorf(x,r)decreases withr, whereas the computational costc(x,r)increases. Typically,rrepresents the number of epochs for training the neural network, but it could also be the training subset size or the number of cross-validation folds.
보다 공식적으로, 목적 함수 f(x,r)가 추가 입력 r∈[r min,r max]를 얻도록 섹션 19.1.1의 정의를 확장하여 지출하려는 자원의 양을 지정합니다. 구성 x의 평가. 오류 f(x,r)는 r에 따라 감소하는 반면 계산 비용 c(x,r)은 증가한다고 가정합니다. 일반적으로 r은 신경망 훈련을 위한 에포크 수를 나타내지만 훈련 하위 집합 크기 또는 교차 검증 접기 수일 수도 있습니다.
from collections import defaultdict
import numpy as np
from scipy import stats
from d2l import torch as d2l
d2l.set_figsize()
위의 코드는 Python 라이브러리와 도구를 가져오고, 그림의 크기를 설정하는 부분입니다.
from collections import defaultdict: Python의 내장 모듈인 collections에서 defaultdict 클래스를 가져옵니다. defaultdict는 기본값(default)을 가진 딕셔너리(dictionary)를 생성하는데 사용됩니다.
import numpy as np: NumPy 라이브러리를 가져옵니다. NumPy는 과학적 계산을 위한 파이썬 라이브러리로 배열(array)과 행렬(matrix) 연산을 지원합니다. np는 NumPy를 짧게 참조하기 위한 별칭(alias)입니다.
from scipy import stats: SciPy 라이브러리에서 stats 모듈을 가져옵니다. SciPy는 과학적 계산을 위한 다양한 기능을 제공하는 라이브러리로, 통계 분석과 관련된 함수와 도구를 포함하고 있습니다.
from d2l import torch as d2l: "d2l" 라이브러리에서 "torch" 모듈을 가져오고, "d2l"을 짧게 참조하기 위한 별칭으로 사용합니다.
d2l.set_figsize(): "d2l" 라이브러리에서 제공하는 함수인 set_figsize()를 호출하여 그림(플롯)의 크기를 설정합니다. 이 함수는 시각화에서 그림의 크기를 조정하는 데 사용됩니다. 크기를 따로 지정하지 않으면 기본 크기로 설정됩니다.
이 코드는 라이브러리를 가져와 사용할 수 있도록 하고, 그림의 크기를 설정하여 나중에 생성할 그래프나 플롯의 크기를 지정합니다.
**다중 신뢰도 하이퍼파라미터 최적화(Multi-Fidelity Hyperparameter Optimization)**는 하이퍼파라미터 최적화를 위한 고급 기술 중 하나로, 기계 학습 모델을 효율적으로 튜닝하기 위해 여러 가지 신뢰도 또는 리소스 수준에서 모델을 평가하는 방법입니다. 일반적으로 이러한 접근 방식은 계산 비용이 높은 고신뢰도(또는 고 리소스) 평가와 계산 비용이 낮은 저신뢰도(또는 저 리소스) 평가를 혼합하여 사용합니다.
다중 신뢰도 하이퍼파라미터 최적화의 핵심 아이디어는 다음과 같습니다.
고신뢰도 평가 (High-Fidelity Evaluation): 높은 신뢰도를 가진 평가는 모델의 성능을 정확하게 측정합니다. 이것은 많은 계산 리소스와 시간을 필요로 합니다. 예를 들어, 모든 훈련 데이터와 에포크 수를 늘리는 것과 같이 모델을 더 오랫동안 훈련하는 것이 포함될 수 있습니다.
저신뢰도 평가 (Low-Fidelity Evaluation): 저신뢰도 평가는 고신뢰도 평가보다 빠르게 수행됩니다. 예를 들어, 더 적은 훈련 데이터를 사용하거나 적은 에포크로 모델을 훈련하는 것이 이에 해당합니다.
리소스 관리: 다중 신뢰도 하이퍼파라미터 최적화는 제한된 계산 리소스 또는 시간 내에서 가장 효율적으로 최적화를 수행하려는 목표를 가지고 있습니다. 이를 위해 리소스를 고정된 신뢰도 평가와 저신뢰도 평가 사이에서 분배합니다.
바람직한 트레이드오프 탐색: 목표는 최적의 하이퍼파라미터 설정을 찾는 것이지만, 모든 경우에 고신뢰도 평가를 수행하는 것은 현실적이지 않을 수 있습니다. 따라서 가용한 리소스 내에서 가능한 한 많은 하이퍼파라미터 설정을 평가하여 최상의 트레이드오프를 찾습니다.
자동화된 하이퍼파라미터 선택: 다중 신뢰도 하이퍼파라미터 최적화는 주어진 리소스 내에서 자동으로 하이퍼파라미터를 선택하고 평가합니다. 이것은 기계 학습 엔지니어 또는 데이터 과학자에게 매우 유용합니다.
다중 신뢰도 하이퍼파라미터 최적화는 하이퍼파라미터 튜닝 과정을 가속화하고 계산 리소스를 효율적으로 활용하면서 최상의 하이퍼파라미터 설정을 찾는 데 도움을 줍니다. 이 방법은 기계 학습 모델의 성능을 최대화하는 데 중요합니다.
19.4.1.Successive Halving
One of the simplest ways to adapt random search to the multi-fidelity setting issuccessive halving(Jamieson and Talwalkar, 2016,Karninet al., 2013). The basic idea is to start withNconfigurations, for example randomly sampled from the configuration space, and to train each of them forr minepochs only. We then discard a fraction of the worst performing trials and train the remaining ones for longer. Iterating this process, fewer trials run for longer, until at least one trial reachesr maxepochs.
다중 충실도 설정에 무작위 검색을 적용하는 가장 간단한 방법 중 하나는 연속적인 반감기입니다(Jamieson and Talwalkar, 2016, Karnin et al., 2013). 기본 아이디어는 예를 들어 구성 공간에서 무작위로 샘플링된 N 구성으로 시작하고 각 구성을 r min epoch 동안만 훈련하는 것입니다. 그런 다음 성능이 가장 낮은 시험 중 일부를 버리고 나머지 시험을 더 오랫동안 훈련합니다. 이 프로세스를 반복하면 적어도 하나의 시도가 r 최대 에포크에 도달할 때까지 더 적은 수의 시도가 더 오랫동안 실행됩니다.
More formally, consider a minimum budgetr min(for example 1 epoch), a maximum budgetr max, for examplemax_epochsin our previous example, and a halving constantη∈{2,3,…}. For simplicity, assume thatr max = r min η**k, withK∈‖. The number of initial configurations is thenN=η**k. Let us define the set of rungsR = {r min, r min η, r min η**2,…,r max}.
보다 공식적으로는 최소 예산 r min(예: 1 epoch), 최대 예산 r max(예: 이전 예의 max_epochs) 및 반감 상수 eta∈{2,3,…}를 고려합니다. 단순화를 위해 r max = r min θ**k, K∈ʼ 라고 가정합니다. 그러면 초기 구성 수는 N=θ**k입니다. 단계 세트 R = {r min, r min eta, r min eta**2,…,r max}를 정의해 보겠습니다.
One round of successive halving proceeds as follows. We start with runningNtrials until the first rungr min. Sorting the validation errors, we keep the top1/ηfraction (which amounts toη**k−1configurations) and discard all the rest. The surviving trials are trained for the next rung (r min ηepochs), and the process is repeated. At each rung, a1/ηfraction of trials survives and their training continues with aηtimes larger budget. With this particular choice ofN, only a single trial will be trained to the full budget r max. Once such a round of successive halving is done, we start the next one with a new set of initial configurations, iterating until the total budget is spent.
한 라운드의 연속적인 반감기는 다음과 같이 진행됩니다. 첫 번째 rung r min까지 N번 시도를 실행하는 것으로 시작합니다. 검증 오류를 정렬하면서 상위 1/eta 부분(eta**k−1 구성에 해당)을 유지하고 나머지는 모두 삭제합니다. 살아남은 시도는 다음 단계(r min θ epochs)에 대해 훈련되고 프로세스가 반복됩니다. 각 단계에서 시행의 1/θ 비율이 살아남고 해당 훈련은 θ배 더 큰 예산으로 계속됩니다. 이 특정 N 선택을 사용하면 단일 시도만 전체 예산 r max로 학습됩니다. 이러한 연속적인 반감기가 완료되면 새로운 초기 구성 세트로 다음 단계를 시작하여 총 예산이 소비될 때까지 반복합니다.
Successive Halving 이란? (GPT 3.5 답변)
Successive Halving은 하이퍼파라미터 최적화에서 사용되는 고급 기술 중 하나입니다. 이 방법은 많은 하이퍼파라미터 구성을 효율적으로 평가하고 가장 우수한 하이퍼파라미터 구성을 찾는 데 도움을 줍니다.
Successive Halving은 다음 단계로 구성됩니다:
초기 라운드 (Initial Round): 먼저 모든 하이퍼파라미터 구성을 동일한 리소스 또는 시간 내에서 평가합니다. 이 단계에서는 많은 하이퍼파라미터 구성을 아직 유망한지 판단하지 않고 각각을 동등하게 다룹니다.
선택 (Selection): 초기 라운드에서 우수한 일부 하이퍼파라미터 구성만 선택합니다. 일반적으로 이것은 상위 N개 구성을 선택하는 것으로 시작합니다. 이 선택 기준은 주로 목표 지표 (예: 정확도 또는 손실)을 기반으로 합니다.
제거 (Elimination): 선택된 하이퍼파라미터 구성 중 일부를 제거합니다. 제거 기준은 각 구성의 상대적 효용성을 평가하는 데 사용됩니다. 이것은 효율성을 높이기 위한 주요 단계로, 낮은 성능을 보이는 하이퍼파라미터 구성을 제거하고 리소스를 더 높은 효과적인 평가로 할당하는 데 도움을 줍니다.
라운드 반복 (Iteration): 선정된 하이퍼파라미터 구성들을 다음 라운드로 이동시킵니다. 이제 리소스 또는 시간을 더욱 증가시켜 더 정확한 평가를 수행합니다. 이 프로세스는 몇 라운드에 걸쳐 반복됩니다.
Successive Halving은 초기에 무작위로 선택된 하이퍼파라미터 구성들을 점진적으로 걸러내고 가장 우수한 구성을 찾기 위해 리소스를 최적으로 활용하는 방법 중 하나입니다. 이 방법은 계산 리소스를 효율적으로 활용하면서도 최상의 하이퍼파라미터 설정을 찾는 데 도움을 줍니다.
We subclass theHPOSchedulerbase class fromSection 19.2in order to implement successive halving, allowing for a genericHPOSearcherobject to sample configurations (which, in our example below, will be aRandomSearcher). Additionally, the user has to pass the minimum resourcer min, the maximum resourcer maxandηas input. Inside our scheduler, we maintain a queue of configurations that still need to be evaluated for the current rungri. We update the queue every time we jump to the next rung.
연속적인 절반 분할을 구현하기 위해 섹션 19.2에서 HPOScheduler 기본 클래스를 서브클래싱하여 일반 HPOSearcher 객체가 샘플 구성(아래 예에서는 RandomSearcher가 됨)을 허용합니다. 또한 사용자는 최소 리소스 r min, 최대 리소스 r max 및 eta를 입력으로 전달해야 합니다. 스케줄러 내에서는 현재 단계에 대해 여전히 평가해야 하는 구성 대기열을 유지 관리합니다. 다음 단계로 이동할 때마다 대기열을 업데이트합니다.
class SuccessiveHalvingScheduler(d2l.HPOScheduler): #@save
def __init__(self, searcher, eta, r_min, r_max, prefact=1):
self.save_hyperparameters()
# Compute K, which is later used to determine the number of configurations
self.K = int(np.log(r_max / r_min) / np.log(eta))
# Define the rungs
self.rung_levels = [r_min * eta ** k for k in range(self.K + 1)]
if r_max not in self.rung_levels:
# The final rung should be r_max
self.rung_levels.append(r_max)
self.K += 1
# Bookkeeping
self.observed_error_at_rungs = defaultdict(list)
self.all_observed_error_at_rungs = defaultdict(list)
# Our processing queue
self.queue = []
위의 코드는 SuccessiveHalvingScheduler라는 클래스를 정의하는 부분입니다. 이 클래스는 하이퍼파라미터 최적화 실험을 위한 스케줄러로 사용됩니다.
def __init__(self, searcher, eta, r_min, r_max, prefact=1):: SuccessiveHalvingScheduler 클래스의 초기화 메서드입니다. 이 클래스는 여러 하이퍼파라미터를 받아 초기화됩니다.
searcher: 하이퍼파라미터 탐색기(searcher) 객체입니다.
eta: 탐색 단계 간의 이동 비율입니다.
r_min: 최소 리소스(예: 시간, 계산 능력)입니다.
r_max: 최대 리소스(예: 시간, 계산 능력)입니다.
prefact: 사전 요소(pre-factored)입니다.
self.K = int(np.log(r_max / r_min) / np.log(eta)): K는 하이퍼파라미터 조합을 조사할 최대 횟수를 나타내는 변수입니다. eta와 리소스 범위에 따라 계산됩니다.
self.rung_levels = [r_min * eta ** k for k in range(self.K + 1)]: rung_levels는 각 단계의 리소스 레벨을 저장하는 리스트입니다. r_min에서 시작하여 eta의 거듭제곱을 계산하여 각 단계의 리소스 레벨을 결정합니다.
if r_max not in self.rung_levels:: r_max가 rung_levels에 포함되지 않으면, r_max를 추가합니다. 이렇게 하여 최종 단계에서도 r_max 리소스를 사용할 수 있도록 합니다.
self.observed_error_at_rungs = defaultdict(list): observed_error_at_rungs는 각 단계에서 관찰된 에러를 저장하기 위한 딕셔너리입니다. 에러는 각 단계에서 계산되고 저장됩니다.
self.all_observed_error_at_rungs = defaultdict(list): all_observed_error_at_rungs는 모든 실험에서 관찰된 에러를 저장하기 위한 딕셔너리입니다. 모든 실험에서 관찰된 에러를 추적합니다.
self.queue = []: 실험을 수행하기 위한 큐(queue)를 초기화합니다. 실험 조합은 이 큐에 추가되어 순차적으로 실행됩니다.
이 클래스는 Successive Halving 알고리즘에 기반하여 하이퍼파라미터 탐색을 수행합니다. 각 단계에서 최적의 하이퍼파라미터 조합을 선택하고, 이를 기반으로 다음 단계의 실험을 수행합니다.
In the beginning our queue is empty, and we fill it withn=prefact⋅η**kconfigurations, which are first evaluated on the smallest rungr min. Here,prefactallows us to reuse our code in a different context. For the purpose of this section, we fixprefact=1. Every time resources become available and theHPOTunerobject queries thesuggestfunction, we return an element from the queue. Once we finish one round of successive halving, which means that we evaluated all surviving configurations on the highest resource levelr maxand our queue is empty, we start the entire process again with a new, randomly sampled set of configurations.
처음에는 대기열이 비어 있으며 n=prefact⋅eta**k 구성으로 채워져 가장 작은 단계 r min에서 먼저 평가됩니다. 여기서 prefact를 사용하면 다른 컨텍스트에서 코드를 재사용할 수 있습니다. 이 섹션의 목적을 위해 prefact=1을 수정합니다. 리소스를 사용할 수 있게 되고 HPOTuner 개체가 제안 기능을 쿼리할 때마다 대기열에서 요소를 반환합니다. 한 라운드의 연속적인 반감기를 마치면, 즉 가장 높은 리소스 수준 r max에서 살아남은 모든 구성을 평가하고 대기열이 비어 있으면 무작위로 샘플링된 새로운 구성 세트로 전체 프로세스를 다시 시작합니다.
@d2l.add_to_class(SuccessiveHalvingScheduler) #@save
def suggest(self):
if len(self.queue) == 0:
# Start a new round of successive halving
# Number of configurations for the first rung:
n0 = int(self.prefact * self.eta ** self.K)
for _ in range(n0):
config = self.searcher.sample_configuration()
config["max_epochs"] = self.r_min # Set r = r_min
self.queue.append(config)
# Return an element from the queue
return self.queue.pop()
위의 코드는 SuccessiveHalvingScheduler 클래스에 suggest 메서드를 추가하는 부분입니다. 이 메서드는 다음 실험에 사용할 하이퍼파라미터 조합을 제안하는 역할을 합니다.
if len(self.queue) == 0:: 큐(queue)가 비어있는 경우, 새로운 Successive Halving 라운드를 시작합니다. 이는 다음 단계에서 실험할 하이퍼파라미터 조합을 선택하는 단계입니다.
n0 = int(self.prefact * self.eta ** self.K): 첫 번째 단계의 실험 횟수(n0)를 계산합니다. prefact와 eta를 사용하여 최초 단계에서 실험할 하이퍼파라미터 조합의 수를 결정합니다.
for _ in range(n0):: 계산된 실험 횟수만큼 반복하여 하이퍼파라미터 조합을 선택하고 큐에 추가합니다.
config["max_epochs"] = self.r_min: 선택한 하이퍼파라미터 조합의 max_epochs 값을 r_min으로 설정합니다. 이렇게 하여 해당 단계에서의 최소 리소스를 사용하게 됩니다.
self.queue.pop(): 큐에서 하이퍼파라미터 조합을 하나씩 꺼내서 반환합니다. 이 조합은 다음 실험에 사용됩니다.
이 메서드는 Successive Halving 알고리즘에 따라 다음 실험에 사용할 하이퍼파라미터 조합을 선택하고, 큐에서 해당 조합을 제거하는 역할을 합니다.
When we collected a new data point, we first update the searcher module. Afterwards we check if we already collect all data points on the current rung. If so, we sort all configurations and push the top1/ηconfigurations into the queue.
새로운 데이터 포인트를 수집하면 먼저 검색 모듈을 업데이트합니다. 그런 다음 현재 단계에서 모든 데이터 포인트를 이미 수집했는지 확인합니다. 그렇다면 모든 구성을 정렬하고 상위 1/eta 구성을 대기열에 푸시합니다.
@d2l.add_to_class(SuccessiveHalvingScheduler) #@save
def update(self, config: dict, error: float, info=None):
ri = int(config["max_epochs"]) # Rung r_i
# Update our searcher, e.g if we use Bayesian optimization later
self.searcher.update(config, error, additional_info=info)
self.all_observed_error_at_rungs[ri].append((config, error))
if ri < self.r_max:
# Bookkeeping
self.observed_error_at_rungs[ri].append((config, error))
# Determine how many configurations should be evaluated on this rung
ki = self.K - self.rung_levels.index(ri)
ni = int(self.prefact * self.eta ** ki)
# If we observed all configuration on this rung r_i, we estimate the
# top 1 / eta configuration, add them to queue and promote them for
# the next rung r_{i+1}
if len(self.observed_error_at_rungs[ri]) >= ni:
kiplus1 = ki - 1
niplus1 = int(self.prefact * self.eta ** kiplus1)
best_performing_configurations = self.get_top_n_configurations(
rung_level=ri, n=niplus1
)
riplus1 = self.rung_levels[self.K - kiplus1] # r_{i+1}
# Queue may not be empty: insert new entries at the beginning
self.queue = [
dict(config, max_epochs=riplus1)
for config in best_performing_configurations
] + self.queue
self.observed_error_at_rungs[ri] = [] # Reset
위의 코드는 SuccessiveHalvingScheduler 클래스에 update 메서드를 추가하는 부분입니다. 이 메서드는 각 실험의 결과를 기반으로 다음 단계의 실험을 업데이트하고 관리합니다.
ri = int(config["max_epochs"]): 현재 실험에서 사용한 max_epochs 값을 가져와 ri 변수에 저장합니다. 이 값은 현재 실험의 리소스 레벨을 나타냅니다.
self.searcher.update(config, error, additional_info=info): searcher 객체를 업데이트합니다. 이는 나중에 베이지안 최적화와 같은 다른 탐색 알고리즘을 사용할 때 유용합니다.
self.all_observed_error_at_rungs[ri].append((config, error)): 모든 실험에서 현재 리소스 레벨 ri에서 관찰된 에러를 저장합니다.
if ri < self.r_max:: 현재 리소스 레벨이 최대 리소스 레벨 r_max보다 작은 경우에만 다음 단계의 처리를 진행합니다.
ki = self.K - self.rung_levels.index(ri): 현재 리소스 레벨에 해당하는 단계 ki를 계산합니다.
ni = int(self.prefact * self.eta ** ki): 현재 단계에서 평가할 실험의 수를 계산합니다.
if len(self.observed_error_at_rungs[ri]) >= ni:: 현재 리소스 레벨에서 이미 모든 실험을 수행한 경우에는 다음 단계로 넘어갑니다.
kiplus1 = ki - 1과 niplus1 = int(self.prefact * self.eta ** kiplus1)을 계산하여 다음 단계에서 평가할 실험의 수와 단계를 결정합니다.
best_performing_configurations = self.get_top_n_configurations(rung_level=ri, n=niplus1): 현재 단계에서 성능이 가장 좋은 상위 niplus1개의 하이퍼파라미터 조합을 가져옵니다.
riplus1 = self.rung_levels[self.K - kiplus1]: 다음 단계의 리소스 레벨 riplus1을 결정합니다.
self.queue = [...] + self.queue: 다음 단계의 실험을 큐에 추가합니다. 이때, 현재 큐에 있는 실험들은 다음 단계의 리소스 레벨로 업데이트되고, 상위 성능 조합들이 추가됩니다.
self.observed_error_at_rungs[ri] = []: 현재 단계에서 관찰된 에러를 리셋하여 다음 단계를 위한 준비를 합니다.
이 메서드는 Successive Halving 알고리즘의 핵심 로직을 구현하며, 각 실험의 결과를 바탕으로 다음 실험의 하이퍼파라미터 조합을 선택하고 큐를 관리합니다.
Configurations are sorted based on their observed performance on the current rung.
구성은 현재 단계에서 관찰된 성능을 기준으로 정렬됩니다.
@d2l.add_to_class(SuccessiveHalvingScheduler) #@save
def get_top_n_configurations(self, rung_level, n):
rung = self.observed_error_at_rungs[rung_level]
if not rung:
return []
sorted_rung = sorted(rung, key=lambda x: x[1])
return [x[0] for x in sorted_rung[:n]]
위의 코드는 SuccessiveHalvingScheduler 클래스에 get_top_n_configurations 메서드를 추가하는 부분입니다. 이 메서드는 주어진 리소스 레벨에서 성능이 가장 좋은 상위 n개 하이퍼파라미터 조합을 반환합니다.
rung = self.observed_error_at_rungs[rung_level]: 주어진 리소스 레벨 rung_level에서 관찰된 모든 실험 결과를 가져옵니다.
if not rung:: 만약 해당 리소스 레벨에서 아직 어떤 실험이 수행되지 않았다면 빈 리스트를 반환합니다.
sorted_rung = sorted(rung, key=lambda x: x[1]): 실험 결과를 성능(에러 값)에 따라 정렬합니다.
return [x[0] for x in sorted_rung[:n]]: 상위 n개의 실험 중 하이퍼파라미터 조합만을 반환합니다. 이는 다음 단계의 실험에 사용됩니다.
즉, get_top_n_configurations 메서드는 주어진 리소스 레벨에서 성능이 가장 좋은 하이퍼파라미터 조합을 선택하는데 사용되며, Successive Halving 알고리즘의 핵심 부분 중 하나입니다.
Let us see how successive halving is doing on our neural network example. We will user min=2,η=2,r max=10, so that rung levels are2,4,8,10.
신경망 예제에서 연속적인 반감기가 어떻게 수행되는지 살펴보겠습니다. r min=2, eta=2, r max=10을 사용하므로 단계 수준은 2,4,8,10이 됩니다.
위의 코드는 Successive Halving HPO (Hyperparameter Optimization) 알고리즘을 설정하는 부분입니다. 이 알고리즘은 하이퍼파라미터 최적화를 위해 사용되며, 주어진 리소스로 가장 좋은 하이퍼파라미터 설정을 찾는 데 도움이 됩니다. 아래는 각 설정과 변수에 대한 설명입니다.
min_number_of_epochs (최소 에포크 수): 각 실험에서 최소 몇 번의 에포크를 실행할지를 나타냅니다. 이 값은 2로 설정되어 있습니다.
max_number_of_epochs (최대 에포크 수): 각 실험에서 최대 몇 번의 에포크까지 실행할지를 나타냅니다. 이 값은 10으로 설정되어 있습니다.
eta (탐색 요인): Successive Halving 알고리즘에서 리소스를 나누는 데 사용되는 요인을 나타냅니다. 이 값은 2로 설정되어 있으며, 리소스가 반으로 줄어들 때마다 실행되는 실험 수가 2배로 증가합니다.
num_gpus (GPU 수): 사용 가능한 GPU 수를 나타냅니다. 이 예제에서는 1로 설정되어 있습니다.
config_space (하이퍼파라미터 공간): 하이퍼파라미터 최적화를 위해 탐색할 하이퍼파라미터 공간을 정의합니다. 여기에서는 학습률(learning_rate)과 배치 크기(batch_size)를 설정하고, 각각 loguniform 및 randint 확률 분포를 사용하여 하이퍼파라미터를 탐색할 범위를 지정합니다.
initial_config (초기 하이퍼파라미터 설정): 최초의 실험을 위해 사용되는 초기 하이퍼파라미터 설정을 나타냅니다. 이 예제에서는 학습률을 0.1로, 배치 크기를 128로 설정합니다.
이러한 설정과 변수들은 Successive Halving HPO 알고리즘을 실행하고 하이퍼파라미터 최적화를 수행하는 데 필요한 매개변수와 공간을 정의합니다.
We just replace the scheduler with our newSuccessiveHalvingScheduler.
위의 코드는 Successive Halving HPO 알고리즘을 구현하고 실행하는 부분입니다. 아래는 코드의 주요 내용에 대한 설명입니다.
searcher: RandomSearcher는 하이퍼파라미터 탐색을 수행하는 데 사용되는 탐색자(searcher)입니다. config_space에서 정의한 하이퍼파라미터 공간을 기반으로 하이퍼파라미터 설정을 무작위로 추출하며, 초기 설정은 initial_config로 설정됩니다.
scheduler: SuccessiveHalvingScheduler는 Successive Halving 알고리즘의 스케줄러입니다. 이 스케줄러는 searcher로부터 샘플된 하이퍼파라미터 설정을 기반으로 실험을 관리하고, eta, r_min, r_max 등을 사용하여 실험을 반복하고 스케줄링합니다.
tuner: HPOTuner는 하이퍼파라미터 최적화를 수행하는 클래스입니다. scheduler를 통해 실험을 관리하고, objective 함수를 사용하여 각 실험의 성능을 평가하며, number_of_trials 매개변수에 지정된 횟수만큼 실험을 실행합니다.
이렇게 구성된 코드는 Successive Halving 알고리즘을 사용하여 하이퍼파라미터 최적화를 수행하며, 30회의 실험을 실행하여 최적의 하이퍼파라미터 설정을 찾습니다.
We can visualize the learning curves of all configurations that we evaluated. Most of the configurations are stopped early and only the better performing configurations survive untilr max. Compare this to vanilla random search, which would allocater maxto every configuration.
평가한 모든 구성의 학습 곡선을 시각화할 수 있습니다. 대부분의 구성은 조기에 중지되며 더 나은 성능의 구성만 r max까지 유지됩니다. 이것을 모든 구성에 r max를 할당하는 바닐라 무작위 검색과 비교해 보세요.
for rung_index, rung in scheduler.all_observed_error_at_rungs.items():
errors = [xi[1] for xi in rung]
d2l.plt.scatter([rung_index] * len(errors), errors)
d2l.plt.xlim(min_number_of_epochs - 0.5, max_number_of_epochs + 0.5)
d2l.plt.xticks(
np.arange(min_number_of_epochs, max_number_of_epochs + 1),
np.arange(min_number_of_epochs, max_number_of_epochs + 1)
)
d2l.plt.ylabel("validation error")
d2l.plt.xlabel("epochs")
위의 코드는 Successive Halving HPO 알고리즘을 통해 각 rung(레벨)에서 관찰된 검증 오차(validation error)를 시각화하는 부분입니다. 아래는 코드의 주요 내용에 대한 설명입니다.
for rung_index, rung in scheduler.all_observed_error_at_rungs.items():: all_observed_error_at_rungs에는 각 rung 레벨에서 관찰된 검증 오차의 정보가 저장되어 있습니다. 이 코드는 각 rung에 대한 정보를 반복하며 그래프를 그립니다.
errors = [xi[1] for xi in rung]: 각 rung에서 관찰된 검증 오차를 추출하여 errors 리스트에 저장합니다.
d2l.plt.scatter([rung_index] * len(errors), errors): rung 레벨에 해당하는 x 좌표에 해당 오차 값을 점으로 표시하여 그래프에 추가합니다.
d2l.plt.xlim(min_number_of_epochs - 0.5, max_number_of_epochs + 0.5): x 축의 범위를 설정합니다.
d2l.plt.xticks(np.arange(min_number_of_epochs, max_number_of_epochs + 1), np.arange(min_number_of_epochs, max_number_of_epochs + 1)): x 축의 눈금을 설정합니다. rung 레벨에 해당하는 값들이 표시됩니다.
d2l.plt.ylabel("validation error")와 d2l.plt.xlabel("epochs"): y 축과 x 축에 라벨을 추가합니다.
이 코드를 통해 각 rung 레벨에서 관찰된 검증 오차를 epochs(에폭)별로 시각적으로 확인할 수 있습니다.
Text(0.5, 0, 'epochs')
Finally, note some slight complexity in our implementation ofSuccessiveHalvingScheduler. Say that a worker is free to run a job, andsuggestis called when the current rung has almost been completely filled, but another worker is still busy with an evaluation. Since we lack the metric value from this worker, we cannot determine the top1/ηfraction to open up the next rung. On the other hand, we want to assign a job to our free worker, so it does not remain idle. Our solution is to start a new round of successive halving and assign our worker to the first trial there. However, once a rung is completed inupdate, we make sure to insert new configurations at the beginning of the queue, so they take precedence over configurations from the next round.
마지막으로 SuccessiveHalvingScheduler 구현이 약간 복잡하다는 점에 유의하세요. 작업자가 작업을 자유롭게 실행할 수 있고 현재 단계가 거의 완전히 채워졌을 때 제안이 호출되지만 다른 작업자는 여전히 평가로 바쁘다고 가정해 보겠습니다. 이 작업자의 메트릭 값이 부족하기 때문에 다음 단계를 열기 위한 상위 1/θ 비율을 결정할 수 없습니다. 반면에 우리는 무료 작업자에게 작업을 할당하여 유휴 상태로 남아 있지 않도록 하려고 합니다. 우리의 해결책은 새로운 연속 반감기 라운드를 시작하고 그곳에서 첫 번째 시도에 작업자를 할당하는 것입니다. 그러나 업데이트에서 단계가 완료되면 대기열 시작 부분에 새 구성을 삽입하여 다음 라운드의 구성보다 우선하도록 합니다.
19.4.2.Summary
In this section, we introduced the concept of multi-fidelity hyperparameter optimization, where we assume to have access to cheap-to-evaluate approximations of the objective function, such as validation error after a certain number of epochs of training as proxy to validation error after the full number of epochs. Multi-fidelity hyperparameter optimization allows to reduce the overall computation of the HPO instead of just reducing the wall-clock time.
이 섹션에서는 다중 충실도 하이퍼파라미터 최적화의 개념을 소개했습니다. 여기서 우리는 검증 오류에 대한 프록시로서 특정 횟수의 훈련 후 검증 오류와 같이 목적 함수의 평가하기 쉬운 근사값에 액세스할 수 있다고 가정합니다. 전체 에포크 수 이후. 다중 충실도 하이퍼파라미터 최적화를 사용하면 벽시계 시간을 줄이는 대신 HPO의 전체 계산을 줄일 수 있습니다.
We implemented and evaluated successive halving, a simple yet efficient multi-fidelity HPO algorithm.
우리는 간단하면서도 효율적인 다중 충실도 HPO 알고리즘인 연속 반감기를 구현하고 평가했습니다.
As we have seen in the previousSection 19.2, we might have to wait hours or even days before random search returns a good hyperparameter configuration, because of the expensive evaluation of hyperparameter configurations. In practice, we have often access to a pool of resources such as multiple GPUs on the same machine or multiple machines with a single GPU. This begs the question:How do we efficiently distribute random search?
이전 섹션 19.2에서 살펴본 것처럼 하이퍼파라미터 구성에 대한 평가 비용이 많이 들기 때문에 무작위 검색이 좋은 하이퍼파라미터 구성을 반환하기까지 몇 시간 또는 심지어 며칠을 기다려야 할 수도 있습니다. 실제로 우리는 동일한 시스템의 여러 GPU 또는 단일 GPU가 있는 여러 시스템과 같은 리소스 풀에 액세스하는 경우가 많습니다. 이는 다음과 같은 질문을 던집니다. 무작위 검색을 어떻게 효율적으로 배포할 수 있습니까?
In general, we distinguish between synchronous and asynchronous parallel hyperparameter optimization (seeFig. 19.3.1). In the synchronous setting, we wait for all concurrently running trials to finish, before we start the next batch. Consider configuration spaces that contain hyperparameters such as the number of filters or number of layers of a deep neural network. Hyperparameter configurations that contain a larger number of layers of filters will naturally take more time to finish, and all other trials in the same batch will have to wait at synchronisation points (grey area inFig. 19.3.1) before we can continue the optimization process.
일반적으로 우리는 동기식 병렬 하이퍼파라미터 최적화와 비동기식 병렬 하이퍼파라미터 최적화를 구별합니다(그림 19.3.1 참조). 동기 설정에서는 다음 배치를 시작하기 전에 동시에 실행 중인 모든 시도가 완료될 때까지 기다립니다. 심층 신경망의 필터 수나 레이어 수와 같은 하이퍼파라미터가 포함된 구성 공간을 고려하세요. 더 많은 수의 필터 레이어를 포함하는 하이퍼파라미터 구성은 당연히 완료하는 데 더 많은 시간이 걸립니다. 그리고 동일한 배치의 다른 모든 시도는 최적화 프로세스를 계속하기 전에 동기화 지점(그림 19.3.1의 회색 영역)에서 기다려야 합니다.
In the asynchronous setting we immediately schedule a new trial as soon as resources become available. This will optimally exploit our resources, since we can avoid any synchronisation overhead. For random search, each new hyperparameter configuration is chosen independently of all others, and in particular without exploiting observations from any prior evaluation. This means we can trivially parallelize random search asynchronously. This is not straight-forward with more sophisticated methods that make decision based on previous observations (seeSection 19.5). While we need access to more resources than in the sequential setting, asynchronous random search exhibits a linear speed-up, in that a certain performance is reachedKtimes faster ifKtrials can be run in parallel.
비동기식 설정에서는 리소스를 사용할 수 있게 되는 즉시 새로운 평가판을 예약합니다. 동기화 오버헤드를 피할 수 있으므로 리소스를 최적으로 활용하게 됩니다. 무작위 검색의 경우 각각의 새로운 하이퍼파라미터 구성은 다른 모든 구성과 독립적으로 선택되며, 특히 이전 평가에서 관찰한 내용을 활용하지 않습니다. 이는 무작위 검색을 비동기적으로 간단하게 병렬화할 수 있음을 의미합니다. 이는 이전 관찰을 기반으로 결정을 내리는 보다 정교한 방법으로는 간단하지 않습니다(19.5절 참조). 순차 설정보다 더 많은 리소스에 액세스해야 하지만 비동기 무작위 검색은 K번 시도를 병렬로 실행할 수 있으면 특정 성능에 K배 더 빠르게 도달한다는 점에서 선형적인 속도 향상을 나타냅니다.
In this notebook, we will look at asynchronous random search that, where trials are executed in multiple python processes on the same machine. Distributed job scheduling and execution is difficult to implement from scratch. We will useSyne Tune(Salinaset al., 2022), which provides us with a simple interface for asynchronous HPO. Syne Tune is designed to be run with different execution back-ends, and the interested reader is invited to study its simple APIs in order to learn more about distributed HPO.
이 노트북에서는 동일한 머신의 여러 Python 프로세스에서 시도가 실행되는 비동기 무작위 검색을 살펴보겠습니다. 분산 작업 스케줄링 및 실행은 처음부터 구현하기 어렵습니다. 비동기식 HPO를 위한 간단한 인터페이스를 제공하는 Syne Tune(Salinas et al., 2022)을 사용하겠습니다. Syne Tune은 다양한 실행 백엔드와 함께 실행되도록 설계되었으며 관심 있는 독자는 분산형 HPO에 대해 자세히 알아보기 위해 간단한 API를 연구하도록 초대됩니다.
import logging
from d2l import torch as d2l
logging.basicConfig(level=logging.INFO)
from syne_tune import StoppingCriterion, Tuner
from syne_tune.backend.python_backend import PythonBackend
from syne_tune.config_space import loguniform, randint
from syne_tune.experiments import load_experiment
from syne_tune.optimizer.baselines import RandomSearch
위의 코드는 하이퍼파라미터 최적화를 위한 도구와 라이브러리를 가져오고 초기 설정을 수행하는 부분입니다. 코드의 주요 내용은 다음과 같습니다:
import logging: 로그 메시지를 기록하기 위한 로깅 모듈을 가져옵니다.
from d2l import torch as d2l: "d2l" 패키지에서 PyTorch 관련 기능을 가져옵니다. 이 패키지는 Deep Learning for Dummies(이해를 돕기 위한 딥 러닝)에서 사용되는 유틸리티 함수와 도구를 제공합니다.
logging.basicConfig(level=logging.INFO): 로깅 레벨을 설정하고 로깅을 초기화합니다. 이 코드는 INFO 레벨 이상의 로그 메시지를 표시하도록 설정합니다.
from syne_tune import StoppingCriterion, Tuner: "syne_tune" 라이브러리에서 하이퍼파라미터 튜닝에 필요한 클래스인 StoppingCriterion과 Tuner를 가져옵니다.
from syne_tune.backend.python_backend import PythonBackend: "syne_tune" 라이브러리에서 하이퍼파라미터 튜닝에 사용되는 백엔드(backend)인 PythonBackend를 가져옵니다.
from syne_tune.config_space import loguniform, randint: 하이퍼파라미터 탐색 공간을 정의하기 위해 "syne_tune" 라이브러리에서 loguniform과 randint 함수를 가져옵니다. 이 함수들을 사용하여 하이퍼파라미터의 분포를 정의할 수 있습니다.
from syne_tune.experiments import load_experiment: 하이퍼파라미터 튜닝 실험을 로드하는 데 사용되는 load_experiment 함수를 가져옵니다.
from syne_tune.optimizer.baselines import RandomSearch: 랜덤 서치 기반의 최적화 알고리즘인 RandomSearch를 가져옵니다.
이 코드는 하이퍼파라미터 튜닝을 위한 다양한 도구와 라이브러리를 가져와 초기 설정을 수행하며, 이후의 코드에서 하이퍼파라미터 튜닝 실험을 진행할 준비를 마칩니다.
INFO:root:SageMakerBackend is not imported since dependencies are missing. You can install them with
pip install 'syne-tune[extra]'
AWS dependencies are not imported since dependencies are missing. You can install them with
pip install 'syne-tune[aws]'
or (for everything)
pip install 'syne-tune[extra]'
AWS dependencies are not imported since dependencies are missing. You can install them with
pip install 'syne-tune[aws]'
or (for everything)
pip install 'syne-tune[extra]'
INFO:root:Ray Tune schedulers and searchers are not imported since dependencies are missing. You can install them with
pip install 'syne-tune[raytune]'
or (for everything)
pip install 'syne-tune[extra]'
19.3.1.Objective Function
First, we have to define a new objective function such that it now returns the performance back to Syne Tune via thereportcallback.
먼저, 보고서 콜백을 통해 성능을 Syne Tune으로 다시 반환하도록 새로운 목적 함수를 정의해야 합니다.
def hpo_objective_lenet_synetune(learning_rate, batch_size, max_epochs):
from syne_tune import Reporter
from d2l import torch as d2l
model = d2l.LeNet(lr=learning_rate, num_classes=10)
trainer = d2l.HPOTrainer(max_epochs=1, num_gpus=1)
data = d2l.FashionMNIST(batch_size=batch_size)
model.apply_init([next(iter(data.get_dataloader(True)))[0]], d2l.init_cnn)
report = Reporter()
for epoch in range(1, max_epochs + 1):
if epoch == 1:
# Initialize the state of Trainer
trainer.fit(model=model, data=data)
else:
trainer.fit_epoch()
validation_error = trainer.validation_error().cpu().detach().numpy()
report(epoch=epoch, validation_error=float(validation_error))
위의 코드는 하이퍼파라미터 최적화 실험을 위한 목표 함수인 hpo_objective_lenet_synetune를 정의하는 부분입니다. 이 함수는 SyneTune 라이브러리를 사용하여 하이퍼파라미터 튜닝을 수행하는데 필요한 내용을 포함하고 있습니다. 코드의 주요 내용은 다음과 같습니다:
from syne_tune import Reporter: SyneTune 라이브러리에서 Reporter 클래스를 가져옵니다. Reporter는 실험 결과를 기록하고 보고하는 데 사용됩니다.
model = d2l.LeNet(lr=learning_rate, num_classes=10): LeNet 아키텍처를 사용하여 모델을 생성합니다. 이때 학습률(learning_rate)은 인자로 전달된 값으로 설정됩니다.
report(epoch=epoch, validation_error=float(validation_error)): 현재 에폭과 검증 오차를 Reporter에 보고합니다.
이러한 과정을 통해 hpo_objective_lenet_synetune 함수는 하이퍼파라미터 최적화 실험을 수행하고, 각 에폭에서의 검증 오차를 기록하여 SyneTune 라이브러리와 함께 사용할 수 있도록 준비합니다.
Note that thePythonBackendof Syne Tune requires dependencies to be imported inside the function definition.
Syne Tune의 PythonBackend를 사용하려면 함수 정의 내로 종속성을 가져와야 합니다.
19.3.2.Asynchronous Scheduler
First, we define the number of workers that evaluate trials concurrently. We also need to specify how long we want to run random search, by defining an upper limit on the total wall-clock time.
먼저, 동시에 시험을 평가하는 작업자 수를 정의합니다. 또한 총 벽시계 시간의 상한을 정의하여 무작위 검색을 실행할 기간을 지정해야 합니다.
n_workers = 2 # Needs to be <= the number of available GPUs
max_wallclock_time = 12 * 60 # 12 minutes
위의 코드는 하이퍼파라미터 최적화 실험에 대한 몇 가지 설정을 지정하는 부분입니다.
n_workers = 2: 병렬로 실행되는 작업자(worker)의 수를 나타냅니다. 이 값은 사용 가능한 GPU 수보다 작거나 같아야 합니다. 즉, 최대로 사용할 GPU 수를 지정합니다. 이 예에서는 2개의 GPU를 사용하도록 설정되어 있습니다.
max_wallclock_time = 12 * 60: 최대 실행 시간을 분 단위로 나타냅니다. 즉, 실험의 최대 실행 시간을 12분으로 설정하고 있습니다. 이 값은 실험을 제한하는 데 사용될 수 있으며, 지정된 시간 내에 실험이 완료되어야 합니다. 실험에 따라 실행 시간이 다를 수 있으므로 적절한 값으로 설정해야 합니다. 이 예에서는 12분으로 설정되어 있으므로 실험이 12분 이내에 완료되어야 합니다.
Next, we state which metric we want to optimize and whether we want to minimize or maximize this metric. Namely,metricneeds to correspond to the argument name passed to thereportcallback.
다음으로, 최적화할 측정항목과 이 측정항목을 최소화할지 최대화할지 여부를 명시합니다. 즉, 측정항목은 보고서 콜백에 전달된 인수 이름과 일치해야 합니다.
mode = "min"
metric = "validation_error"
위의 코드는 하이퍼파라미터 튜닝 실험에서 사용되는 최적화 모드와 평가 지표를 설정하는 부분입니다.
mode = "min": 최적화 모드를 나타내는 변수입니다. "min"으로 설정되어 있으므로 최적화 과정에서는 평가 지표(metric)를 최소화하는 방향으로 진행됩니다. 다시 말해, 모델의 성능을 향상시키는 방향으로 하이퍼파라미터가 조절될 것입니다. 이는 일반적으로 검증 오차나 손실 함수를 최소화하는 경우에 사용됩니다.
metric = "validation_error": 평가 지표를 나타내는 변수입니다. 이 변수는 최적화 모드에 따라 설정되며, 실험 중에 평가되는 지표를 나타냅니다. 이 경우 "validation_error"로 설정되어 있으므로 검증 오차를 평가 지표로 사용하여 최적화를 수행할 것입니다. 검증 오차를 최소화하도록 하이퍼파라미터를 조절하는 것이 목표입니다.
이러한 설정은 하이퍼파라미터 튜닝 실험의 목표와 방향성을 결정하는 중요한 요소 중 하나입니다.
We use the configuration space from our previous example. In Syne Tune, this dictionary can also be used to pass constant attributes to the training script. We make use of this feature in order to passmax_epochs. Moreover, we specify the first configuration to be evaluated ininitial_config.
이전 예제의 구성 공간을 사용합니다. Syne Tune에서 이 사전을 사용하여 상수 속성을 교육 스크립트에 전달할 수도 있습니다. max_epochs를 전달하기 위해 이 기능을 사용합니다. 또한,initial_config에서 평가할 첫 번째 구성을 지정합니다.
위의 코드는 하이퍼파라미터 튜닝 실험을 위한 하이퍼파라미터 공간과 초기 설정을 정의하는 부분입니다.
config_space: 하이퍼파라미터 탐색 공간을 나타내는 딕셔너리입니다. 이 공간에는 다양한 하이퍼파라미터의 범위나 분포를 정의할 수 있습니다. 여기서 정의된 하이퍼파라미터는 다음과 같습니다:
"learning_rate": 학습률(learning rate)을 나타냅니다. loguniform(1e-2, 1)은 0.01에서 1 사이의 로그 균등 분포를 가진 값을 의미합니다.
"batch_size": 미니배치 크기(batch size)를 나타냅니다. randint(32, 256)은 32에서 256 사이의 정수 값을 나타냅니다.
"max_epochs": 최대 에폭 수를 나타냅니다. 여기서는 고정값으로 10으로 설정되어 있습니다.
initial_config: 초기 하이퍼파라미터 설정을 나타내는 딕셔너리입니다. 실험의 초기 단계에서 사용할 하이퍼파라미터 설정을 정의합니다. 이 설정은 실험의 초기값으로 사용됩니다. 여기서는 다음과 같은 하이퍼파라미터 설정을 가지고 있습니다:
"learning_rate": 초기 학습률을 0.1로 설정합니다.
"batch_size": 초기 미니배치 크기를 128로 설정합니다.
이렇게 정의된 하이퍼파라미터 공간과 초기 설정은 하이퍼파라미터 튜닝 실험을 수행할 때 사용됩니다. 실험은 이 하이퍼파라미터 공간에서 하이퍼파라미터를 샘플링하고, 초기 설정을 시작점으로 하여 최적의 하이퍼파라미터 조합을 찾게 됩니다.
Next, we need to specify the back-end for job executions. Here we just consider the distribution on a local machine where parallel jobs are executed as sub-processes. However, for large scale HPO, we could run this also on a cluster or cloud environment, where each trial consumes a full instance.
다음으로 작업 실행을 위한 백엔드를 지정해야 합니다. 여기서는 병렬 작업이 하위 프로세스로 실행되는 로컬 시스템에서의 배포를 고려합니다. 그러나 대규모 HPO의 경우 각 평가판이 전체 인스턴스를 사용하는 클러스터 또는 클라우드 환경에서도 이를 실행할 수 있습니다.
위의 코드는 실험을 실행하기 위해 SyneTune 라이브러리의 Python 백엔드를 설정하는 부분입니다.
trial_backend = PythonBackend(...): PythonBackend는 SyneTune 라이브러리에서 실험을 수행하기 위한 백엔드(실행 환경)를 설정하는데 사용됩니다. 이 백엔드는 Python 코드를 실행하는 데 사용됩니다.
tune_function=hpo_objective_lenet_synetune: tune_function 매개변수에는 하이퍼파라미터 최적화 실험을 실행할 함수를 지정합니다. 여기서는 hpo_objective_lenet_synetune 함수를 지정하여 이 함수가 하이퍼파라미터 튜닝을 수행하도록 합니다.
config_space=config_space: config_space 매개변수에는 하이퍼파라미터 탐색 공간을 지정합니다. 이전에 정의한 config_space 딕셔너리가 여기에 사용됩니다.
이렇게 설정된 trial_backend는 하이퍼파라미터 최적화 실험을 실행하는 데 필요한 백엔드 설정을 가지고 있습니다. 이제 이 백엔드를 사용하여 하이퍼파라미터 튜닝 실험을 실행할 수 있습니다.
We can now create the scheduler for asynchronous random search, which is similar in behaviour to ourBasicSchedulerfromSection 19.2.
이제 섹션 19.2의 BasicScheduler와 동작이 유사한 비동기 무작위 검색을 위한 스케줄러를 생성할 수 있습니다.
위의 코드는 랜덤 서치(Random Search)를 사용하여 하이퍼파라미터 튜닝 실험을 설정하는 부분입니다.
scheduler = RandomSearch(...): RandomSearch는 랜덤 서치 최적화 전략을 사용하는 스케줄러를 설정하는데 사용됩니다.
config_space: config_space 매개변수에는 하이퍼파라미터 탐색 공간을 지정합니다. 이전에 정의한 config_space 딕셔너리가 여기에 사용됩니다.
metric=metric: metric 매개변수에는 평가 지표(metric)를 지정합니다. 이전에 설정한 metric 변수가 사용됩니다. 이 경우 "validation_error"가 평가 지표로 사용됩니다.
mode=mode: mode 매개변수에는 최적화 모드를 지정합니다. 이전에 설정한 mode 변수가 사용됩니다. 이 경우 "min" 모드로 설정되어 있으므로 평가 지표를 최소화하려고 시도할 것입니다.
points_to_evaluate=[initial_config]: points_to_evaluate 매개변수에는 초기 하이퍼파라미터 설정을 포함하는 리스트를 지정합니다. 이전에 정의한 initial_config 변수가 사용됩니다. 이는 랜덤 서치의 시작점으로 사용됩니다.
이렇게 설정된 scheduler는 랜덤 서치 최적화 전략을 사용하여 하이퍼파라미터 탐색을 수행합니다. 랜덤 서치는 주어진 하이퍼파라미터 탐색 공간에서 무작위로 하이퍼파라미터 조합을 샘플링하고, 해당 조합을 평가하여 최적의 조합을 찾는 방법 중 하나입니다.
INFO:syne_tune.optimizer.schedulers.fifo:max_resource_level = 10, as inferred from config_space
INFO:syne_tune.optimizer.schedulers.fifo:Master random_seed = 2737092907
Syne Tune also features aTuner, where the main experiment loop and bookkeeping is centralized, and interactions between scheduler and back-end are mediated.
Syne Tune에는 주요 실험 루프와 장부를 중앙 집중화하고 스케줄러와 백엔드 간의 상호 작용을 중재하는 튜너 기능도 있습니다.
stop_criterion = StoppingCriterion(max_wallclock_time=max_wallclock_time): StoppingCriterion은 실험이 종료될 조건을 설정하는데 사용됩니다. 이전에 정의한 max_wallclock_time 변수를 사용하여 실험이 최대 실행 시간을 초과하면 종료되도록 설정합니다.
tuner = Tuner(...): Tuner 객체를 설정합니다. 이 객체는 하이퍼파라미터 튜닝 실험을 제어하고 실행하는 역할을 합니다.
trial_backend: trial_backend 매개변수에는 실험을 실행할 백엔드(실행 환경)를 지정합니다. 이전에 설정한 trial_backend가 사용됩니다.
scheduler: scheduler 매개변수에는 스케줄러를 지정합니다. 이전에 설정한 scheduler가 사용됩니다.
stop_criterion: stop_criterion 매개변수에는 실험이 종료될 조건을 지정합니다. 이전에 설정한 stop_criterion이 사용됩니다.
n_workers: n_workers 매개변수에는 병렬로 실행될 작업자(worker)의 수를 지정합니다. 이전에 설정한 n_workers 변수가 사용됩니다.
print_update_interval: print_update_interval 매개변수에는 실험 진행 상황을 출력할 간격을 설정합니다. 여기서는 최대 실행 시간의 60%에 해당하는 간격으로 설정되어 있습니다.
Tuner 객체는 설정된 백엔드와 스케줄러를 사용하여 하이퍼파라미터 튜닝 실험을 실행하며, 종료 조건이 충족되거나 최대 실행 시간이 초과되면 실험을 종료합니다.
Let us run our distributed HPO experiment. According to our stopping criterion, it will run for about 12 minutes.
분산된 HPO 실험을 실행해 보겠습니다. 우리의 중지 기준에 따르면 약 12분 동안 실행됩니다.
tuner.run()
위의 코드는 설정된 Tuner 객체를 사용하여 하이퍼파라미터 튜닝 실험을 실행하는 부분입니다.
tuner.run() 함수를 호출하면 하이퍼파라미터 튜닝 실험이 시작됩니다. Tuner 객체는 설정된 백엔드와 스케줄러를 사용하여 하이퍼파라미터 공간에서 하이퍼파라미터 조합을 샘플링하고, 각 조합을 평가하여 최적의 하이퍼파라미터 조합을 찾습니다. 설정된 종료 조건이 충족되거나 최대 실행 시간이 초과되면 실험을 종료하고 최적의 하이퍼파라미터 조합 및 결과를 반환합니다.
실험 진행 상황은 이전에 설정한 print_update_interval 간격으로 출력될 것입니다. 이 코드를 실행하면 하이퍼파라미터 튜닝 실험이 실행되고, 최적의 모델을 찾기 위해 하이퍼파라미터 조합을 탐색하게 됩니다.
The logs of all evaluated hyperparameter configurations are stored for further analysis. At any time during the tuning job, we can easily get the results obtained so far and plot the incumbent trajectory.
평가된 모든 하이퍼파라미터 구성의 로그는 추가 분석을 위해 저장됩니다. 튜닝 작업 중 언제든지 지금까지 얻은 결과를 쉽게 얻고 기존 궤적을 그릴 수 있습니다.
19.3.3.Visualize the Asynchronous Optimization Process
Below we visualize how the learning curves of every trial (each color in the plot represents a trial) evolve during the asynchronous optimization process. At any point in time, there are as many trials running concurrently as we have workers. Once a trial finishes, we immediately start the next trial, without waiting for the other trials to finish. Idle time of workers is reduced to a minimum with asynchronous scheduling.
아래에서는 비동기 최적화 프로세스 동안 모든 시행(플롯의 각 색상은 시행을 나타냄)의 학습 곡선이 어떻게 발전하는지 시각화합니다. 어느 시점에서든 작업자 수만큼 동시에 실행되는 시험이 많이 있습니다. 시험이 끝나면 다른 시험이 끝날 때까지 기다리지 않고 즉시 다음 시험을 시작합니다. 비동기 스케줄링으로 작업자의 유휴 시간을 최소화합니다.
이 코드는 각 실험 트라이얼의 실행 시간에 따른 목표 함수(여기서는 검증 오차)의 변화를 시각화합니다. 실험 결과를 통해 어떤 하이퍼파라미터 조합이 더 나은 성능을 보이는지를 파악할 수 있습니다.
Text(0, 0.5, 'objective function')
19.3.4.Summary
We can reduce the waiting time for random search substantially by distribution trials across parallel resources. In general, we distinguish between synchronous scheduling and asynchronous scheduling. Synchronous scheduling means that we sample a new batch of hyperparameter configurations once the previous batch finished. If we have a stragglers - trials that takes more time to finish than other trials - our workers need to wait at synchronization points. Asynchronous scheduling evaluates a new hyperparameter configurations as soon as resources become available, and, hence, ensures that all workers are busy at any point in time. While random search is easy to distribute asynchronously and does not require any change of the actual algorithm, other methods require some additional modifications.
병렬 리소스 전반에 걸친 배포 시도를 통해 무작위 검색 대기 시간을 크게 줄일 수 있습니다. 일반적으로 동기 스케줄링과 비동기 스케줄링을 구분합니다. 동기식 스케줄링은 이전 배치가 완료되면 새로운 하이퍼파라미터 구성 배치를 샘플링하는 것을 의미합니다. 다른 시도보다 완료하는 데 더 많은 시간이 걸리는 시도인 낙오자가 있는 경우 작업자는 동기화 지점에서 기다려야 합니다. 비동기식 스케줄링은 리소스를 사용할 수 있게 되는 즉시 새로운 하이퍼파라미터 구성을 평가하므로 모든 작업자가 언제든지 바쁜 상태가 되도록 보장합니다. 무작위 검색은 비동기적으로 배포하기 쉽고 실제 알고리즘을 변경할 필요가 없지만 다른 방법에는 몇 가지 추가 수정이 필요합니다.
19.3.5.Exercises
Consider theDropoutMLPmodel implemented inSection 5.6, and used in Exercise 1 ofSection 19.2.
Implement an objective functionhpo_objective_dropoutmlp_synetuneto be used with Syne Tune. Make sure that your function reports the validation error after every epoch.
Using the setup of Exercise 1 inSection 19.2, compare random search to Bayesian optimization. If you use SageMaker, feel free to use Syne Tune’s benchmarking facilities in order to run experiments in parallel. Hint: Bayesian optimization is provided assyne_tune.optimizer.baselines.BayesianOptimization.
For this exercise, you need to run on an instance with at least 4 CPU cores. For one of the methods used above (random search, Bayesian optimization), run experiments withn_workers=1,n_workers=2,n_workers=4, and compare results (incumbent trajectories). At least for random search, you should observe linear scaling with respect to the number of workers. Hint: For robust results, you may have to average over several repetitions each.
Advanced. The goal of this exercise is to implement a new scheduler in Syne Tune.
Create a virtual environment containing both thed2lbookandsyne-tunesources.
Implement theLocalSearcherfrom Exercise 2 inSection 19.2as a new searcher in Syne Tune. Hint: Readthis tutorial. Alternatively, you may follow thisexample.
Compare your newLocalSearcherwithRandomSearchon theDropoutMLPbenchmark.
Before we dive into the methodology, we will first discuss a basic code structure that allows us to efficiently implement various HPO algorithms. In general, all HPO algorithms considered here need to implement two decision making primitives,searchingandscheduling. First, they need to sample new hyperparameter configurations, which often involves some kind of search over the configuration space. Second, for each configuration, an HPO algorithm needs to schedule its evaluation and decide how many resources to allocate for it. Once we start to evaluate a configuration, we will refer to it as atrial. We map these decisions to two classes,HPOSearcherandHPOScheduler. On top of that, we also provide aHPOTunerclass that executes the optimization process.
방법론에 대해 알아보기 전에 먼저 다양한 HPO 알고리즘을 효율적으로 구현할 수 있는 기본 코드 구조에 대해 논의하겠습니다. 일반적으로 여기에서 고려되는 모든 HPO 알고리즘은 검색 searching과 예약 scheduling이라는 두 가지 의사 결정 기본 요소를 구현해야 합니다. 첫째, 새로운 하이퍼파라미터 구성을 샘플링해야 하며, 여기에는 종종 구성 공간에 대한 일종의 검색이 포함됩니다. 둘째, 각 구성에 대해 HPO 알고리즘은 평가 일정을 계획하고 이에 할당할 리소스 수를 결정해야 합니다. 구성 평가를 시작하면 이를 평가판이라고 합니다. 우리는 이러한 결정을 HPOSearcher와 HPOScheduler라는 두 클래스에 매핑합니다. 또한 최적화 프로세스를 실행하는 HPOTuner 클래스도 제공합니다.
이 스케줄러 및 검색기 개념은 Syne Tune(Salinas et al., 2022), Ray Tune(Liaw et al., 2018) 또는 Optuna(Akiba et al., 2019)와 같은 인기 있는 HPO 라이브러리에서도 구현됩니다.
import time
from scipy import stats
from d2l import torch as d2l
위의 코드는 파이썬 모듈 및 라이브러리를 가져오는 부분입니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
import time: 파이썬의 내장 모듈인 time 모듈을 가져옵니다. 이 모듈은 시간과 관련된 함수를 제공하며, 시간 지연(delay)을 생성하거나 시간을 측정하는 데 사용될 수 있습니다.
from scipy import stats: SciPy 라이브러리에서 stats 모듈을 가져옵니다. SciPy는 과학 및 공학 연산을 위한 파이썬 라이브러리로, 통계, 최적화, 선형 대수, 신호 처리 등 다양한 기능을 제공합니다. 여기서는 주로 확률 분포와 통계 함수를 사용하는 stats 모듈을 가져옵니다.
from d2l import torch as d2l: "Dive into Deep Learning" (D2L) 도서와 관련된 PyTorch 유틸리티 함수 및 도움말 기능을 제공하는 d2l 라이브러리에서 PyTorch 모듈을 가져옵니다. 이 라이브러리는 딥러닝 교육 및 예제 코드를 지원하는 데 사용됩니다. 여기서는 d2l 라이브러리에서 torch 모듈을 가져와서 d2l로 별칭을 붙입니다.
이 코드는 필요한 모듈과 라이브러리를 가져와서 이후의 작업에서 사용할 수 있도록 설정하는 부분입니다.
Hyperparameter Optimization API란 (GPT 3.5 답변)
The Hyperparameter Optimization (HPO) API refers to a programming interface or toolset that allows developers and data scientists to perform hyperparameter optimization efficiently and effectively. This API typically includes functions, classes, and methods for configuring, running, and managing the hyperparameter optimization process.
하이퍼파라미터 최적화 (HPO) API는 개발자와 데이터 과학자가 하이퍼파라미터 최적화를 효율적이고 효과적으로 수행할 수 있도록 하는 프로그래밍 인터페이스 또는 도구 세트를 가리킵니다. 이 API에는 하이퍼파라미터 최적화 프로세스를 구성하고 실행하며 관리하는 데 사용되는 함수, 클래스 및 메서드가 포함됩니다.
Here are some key components and functionalities often found in an HPO API:
다음은 일반적으로 HPO API에서 찾을 수 있는 주요 구성 요소와 기능입니다.
Hyperparameter Configuration: The API allows users to define the hyperparameters they want to optimize and specify their respective search spaces. This involves specifying the range of values or distribution from which each hyperparameter should be sampled during optimization.
하이퍼파라미터 구성: API를 통해 사용자는 최적화하려는 하이퍼파라미터를 정의하고 해당 하이퍼파라미터의 검색 공간을 지정할 수 있습니다. 이는 각 하이퍼파라미터의 최적화 중에 샘플링해야 하는 값 또는 분포를 지정하는 것을 포함합니다.
Objective Function: Users can define the objective function (also known as the loss or evaluation function) that quantifies the performance of a machine learning model with a given set of hyperparameters. The API provides a way to evaluate the model's performance using this function.
목적 함수: 사용자는 주어진 하이퍼파라미터 세트에 대한 기계 학습 모델의 성능을 양적화하는 목적 함수(손실 또는 평가 함수로도 알려짐)를 정의할 수 있습니다. API는 이 함수를 사용하여 모델의 성능을 평가하는 방법을 제공합니다.
Optimization Algorithms: The API offers a selection of optimization algorithms such as random search, Bayesian optimization, genetic algorithms, or more advanced techniques. Users can choose the algorithm that best suits their optimization problem.
최적화 알고리즘: API는 무작위 검색, 베이지안 최적화, 유전 알고리즘 또는 더 고급 기술과 같은 최적화 알고리즘을 선택할 수 있는 옵션을 제공합니다. 사용자는 최적화 문제에 가장 적합한 알고리즘을 선택할 수 있습니다.
Concurrency and Parallelism: It provides options for running multiple hyperparameter optimization trials concurrently or in parallel. This can significantly speed up the optimization process, especially when optimizing computationally expensive models.
동시성과 병렬 처리: 일부 HPO API는 여러 하이퍼파라미터 최적화 시행을 동시에 또는 병렬로 실행하는 옵션을 제공합니다. 이렇게 하면 특히 계산 비용이 많이 드는 모델을 최적화할 때 최적화 프로세스가 크게 가속화될 수 있습니다.
Early Stopping and Resource Management: Some HPO APIs support early stopping criteria based on the performance of ongoing trials. They can also manage resources, ensuring that the optimization process respects hardware constraints.
조기 중지 및 리소스 관리: 일부 HPO API는 진행 중인 시행의 성능을 기반으로 조기 중지 기준을 지원합니다. 또한 하드웨어 제한을 존중하도록 최적화 프로세스를 관리할 수 있습니다.
Results Tracking: The API may include functions for tracking and storing the results of each optimization trial, including hyperparameters, evaluation metrics, and other relevant information.
결과 추적: API에는 각 하이퍼파라미터 최적화 시행의 결과를 추적하고 저장하는 함수가 포함될 수 있으며, 이는 하이퍼파라미터, 평가 메트릭, 기타 관련 정보를 포함합니다.
Visualization and Reporting: It often includes tools for visualizing the progress of hyperparameter optimization, such as plots or graphs showing how the objective function changes over time.
시각화 및 보고: 종종 하이퍼파라미터 최적화 진행 상황을 시각화하는 데 사용되는 도구를 포함하며, 이러한 도구는 목적 함수가 어떻게 시간에 따라 변하는지 보여주는 플롯이나 그래프를 제공합니다.
Integration with Machine Learning Frameworks: Many HPO APIs are designed to seamlessly integrate with popular machine learning libraries and frameworks like TensorFlow, PyTorch, scikit-learn, and others.
기계 학습 프레임워크 통합: 많은 HPO API는 인기 있는 기계 학습 라이브러리와 프레임워크인 TensorFlow, PyTorch, scikit-learn 및 기타 라이브러리와 원활하게 통합될 수 있도록 설계되었습니다.
Customization: Advanced users can customize the optimization process by specifying custom algorithms, resource management policies, or search strategies.
사용자 정의: 고급 사용자는 사용자 지정 알고리즘, 리소스 관리 정책 또는 검색 전략을 지정하여 최적화 프로세스를 사용자 정의할 수 있습니다.
Hyperparameter Importance Analysis: Some APIs offer functionality for analyzing the importance of different hyperparameters in the final model's performance.
하이퍼파라미터 중요도 분석: 일부 API는 최종 모델 성능에 대한 다른 하이퍼파라미터의 중요성을 분석하는 기능을 제공합니다.
Overall, the HPO API simplifies the process of hyperparameter tuning, allowing users to find optimal hyperparameter settings for their machine learning models more efficiently. It can save time and computational resources and help improve model performance. Popular examples of HPO APIs include Optuna, Hyperopt, and Ray Tune, among others.
전반적으로 HPO API는 하이퍼파라미터 튜닝 프로세스를 단순화하여 사용자가 기계 학습 모델의 최적 하이퍼파라미터 설정을 보다 효율적으로 찾을 수 있게 해줍니다. 시간과 계산 리소스를 절약하고 모델 성능을 향상시킬 수 있습니다. 인기 있는 HPO API 예제로는 Optuna, Hyperopt, Ray Tune 등이 있습니다.
19.2.1.Searcher
Below we define a base class for searchers, which provides a new candidate configuration through thesample_configurationfunction. A simple way to implement this function would be to sample configurations uniformly at random, as we did for random search inSection 19.1. More sophisticated algorithms, such as Bayesian optimization, will make these decisions based on the performance of previous trials. As a result, these algorithms are able to sample more promising candidates over time. We add theupdatefunction in order to update the history of previous trials, which can then be exploited to improve our sampling distribution.
아래에서는 Sample_configuration 함수를 통해 새로운 후보 구성을 제공하는 검색자를 위한 기본 클래스를 정의합니다. 이 기능을 구현하는 간단한 방법은 섹션 19.1에서 무작위 검색을 수행한 것처럼 무작위로 균일하게 구성을 샘플링하는 것입니다. 베이지안 최적화와 같은 보다 정교한 알고리즘은 이전 시도의 성능을 기반으로 이러한 결정을 내립니다. 결과적으로 이러한 알고리즘은 시간이 지남에 따라 더 유망한 후보자를 샘플링할 수 있습니다. 이전 시도의 기록을 업데이트하기 위해 업데이트 기능을 추가한 다음 샘플링 분포를 개선하는 데 활용할 수 있습니다.
위의 코드는 하이퍼파라미터(Hyperparameters) 탐색을 수행하는 클래스인 HPOSearcher를 정의하는 부분입니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
class HPOSearcher(d2l.HyperParameters):: HPOSearcher 클래스를 정의합니다. 이 클래스는 d2l.HyperParameters 클래스를 상속합니다.
def sample_configuration() -> dict:: 하이퍼파라미터 탐색 과정에서 하이퍼파라미터 구성(configuration)을 샘플링하는 메서드를 정의합니다. 이 메서드는 하이퍼파라미터 탐색 알고리즘이 다음으로 시도할 하이퍼파라미터 구성을 생성하고 이를 딕셔너리 형태로 반환해야 합니다. 이 메서드는 추상 메서드로 구현되지 않으며 하위 클래스에서 구현되어야 합니다.
def update(self, config: dict, error: float, additional_info=None):: 하이퍼파라미터 탐색 알고리즘이 하이퍼파라미터 구성을 시도한 후에, 해당 구성에 대한 결과인 검증 오차와 추가 정보를 기반으로 어떤 동작을 수행해야 하는지를 정의하는 메서드입니다. 이 메서드는 하이퍼파라미터 탐색 알고리즘이 현재 시도한 하이퍼파라미터 구성(config), 해당 구성에 대한 검증 오차(error), 그리고 추가 정보(additional_info)를 인자로 받습니다. 이 메서드는 추상 메서드로 구현되지 않으며 하위 클래스에서 구현되어야 합니다.
HPOSearcher 클래스는 하이퍼파라미터 탐색 과정에서 필요한 메서드를 정의하는 기본 클래스입니다. 실제 하이퍼파라미터 탐색을 위해서는 이 클래스를 상속하고 sample_configuration과 update 메서드를 구현해야 합니다. 이 클래스를 상속한 하위 클래스에서는 하이퍼파라미터 탐색 알고리즘에 따라 구체적인 동작을 정의하게 됩니다.
The following code shows how to implement our random search optimizer from the previous section in this API. As a slight extension, we allow the user to prescribe the first configuration to be evaluated viainitial_config, while subsequent ones are drawn at random.
다음 코드는 이 API의 이전 섹션에서 무작위 검색 최적화 프로그램을 구현하는 방법을 보여줍니다. 약간의 확장으로 사용자가 초기 구성을 통해 평가할 첫 번째 구성을 규정하고 후속 구성은 무작위로 그릴 수 있습니다.
class RandomSearcher(HPOSearcher): #@save
def __init__(self, config_space: dict, initial_config=None):
self.save_hyperparameters()
def sample_configuration(self) -> dict:
if self.initial_config is not None:
result = self.initial_config
self.initial_config = None
else:
result = {
name: domain.rvs()
for name, domain in self.config_space.items()
}
return result
위의 코드는 랜덤 탐색(Random Search)을 수행하는 RandomSearcher 클래스를 정의하는 부분입니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
class RandomSearcher(HPOSearcher):: RandomSearcher 클래스를 정의합니다. 이 클래스는 HPOSearcher 클래스를 상속합니다.
def __init__(self, config_space: dict, initial_config=None):: RandomSearcher 클래스의 생성자 메서드를 정의합니다. 이 생성자 메서드는 두 개의 매개변수를 입력으로 받습니다.
config_space: 하이퍼파라미터 탐색을 위한 하이퍼파라미터 공간을 나타내는 딕셔너리입니다. 각 하이퍼파라미터의 이름과 확률 분포가 포함되어 있어야 합니다.
initial_config: 초기 하이퍼파라미터 구성을 나타내는 딕셔너리입니다. 기본값은 None으로 설정되어 있습니다.
self.save_hyperparameters(): 하이퍼파라미터를 저장하는 메서드입니다. 이 메서드를 호출하여 RandomSearcher 클래스의 하이퍼파라미터를 저장합니다.
def sample_configuration(self) -> dict:: 하이퍼파라미터 탐색 과정에서 하이퍼파라미터 구성을 랜덤하게 샘플링하는 메서드를 정의합니다. 이 메서드는 딕셔너리 형태로 하이퍼파라미터 구성을 반환해야 합니다.
처음에 initial_config가 설정되어 있다면, 초기 구성을 사용하고 initial_config를 None으로 설정합니다.
그렇지 않으면, config_space에 정의된 각 하이퍼파라미터에 대해 해당 확률 분포(domain)에서 랜덤하게 값을 샘플링하여 딕셔너리로 구성합니다.
최종적으로 샘플링된 하이퍼파라미터 구성을 반환합니다.
RandomSearcher 클래스는 랜덤 탐색을 수행하는 클래스로, sample_configuration 메서드에서 랜덤하게 하이퍼파라미터를 선택하여 반환합니다. 이를 통해 하이퍼파라미터 탐색을 무작위로 수행하는 간단한 탐색 전략을 구현할 수 있습니다.
19.2.2.Scheduler
Beyond sampling configurations for new trials, we also need to decide when and for how long to run a trial. In practice, all these decisions are done by theHPOScheduler, which delegates the choice of new configurations to aHPOSearcher. Thesuggestmethod is called whenever some resource for training becomes available. Apart from invokingsample_configurationof a searcher, it may also decide upon parameters likemax_epochs(i.e., how long to train the model for). Theupdatemethod is called whenever a trial returns a new observation.
새로운 시험을 위한 샘플링 구성 외에도 시험을 실행할 시기와 기간도 결정해야 합니다. 실제로 이러한 모든 결정은 새로운 구성 선택을 HPOSearcher에 위임하는 HPOScheduler에 의해 수행됩니다. 훈련을 위한 리소스를 사용할 수 있을 때마다 제안 메소드가 호출됩니다. 검색기의 Sample_configuration을 호출하는 것 외에도 max_epochs(즉, 모델을 훈련할 기간)와 같은 매개변수를 결정할 수도 있습니다. 업데이트 메소드는 시행에서 새로운 관찰이 반환될 때마다 호출됩니다.
위의 코드는 하이퍼파라미터(Hyperparameters) 탐색을 위한 스케줄러인 HPOScheduler 클래스를 정의하는 부분입니다. 이 클래스는 하이퍼파라미터 탐색 과정에서 다양한 하이퍼파라미터 탐색 알고리즘과 스케줄링을 구현하기 위한 기반 클래스입니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
class HPOScheduler(d2l.HyperParameters):: HPOScheduler 클래스를 정의합니다. 이 클래스는 d2l.HyperParameters 클래스를 상속합니다.
def suggest(self) -> dict:: 하이퍼파라미터 탐색 알고리즘이 다음으로 시도할 하이퍼파라미터 구성(configuration)을 제안하는 메서드를 정의합니다. 이 메서드는 추상 메서드로 구현되지 않으며 하위 클래스에서 구현되어야 합니다. 구체적인 하이퍼파라미터 탐색 알고리즘에 따라 다음 시도할 하이퍼파라미터 구성을 반환합니다.
def update(self, config: dict, error: float, info=None):: 하이퍼파라미터 탐색 알고리즘이 하이퍼파라미터 구성을 시도한 후에, 해당 구성에 대한 결과인 검증 오차와 추가 정보를 기반으로 어떤 동작을 수행해야 하는지를 정의하는 메서드입니다. 이 메서드는 추상 메서드로 구현되지 않으며 하위 클래스에서 구현되어야 합니다. 검증 오차와 추가 정보를 활용하여 하이퍼파라미터 탐색 알고리즘의 스케줄링 및 업데이트 동작을 정의합니다.
HPOScheduler 클래스는 다양한 하이퍼파라미터 탐색 알고리즘과 스케줄링 전략을 구현하기 위한 기반 클래스로 사용될 수 있습니다. 구체적인 하이퍼파라미터 탐색 알고리즘에 따라 suggest와 update 메서드를 하위 클래스에서 구현하여 사용할 수 있습니다.
To implement random search, but also other HPO algorithms, we only need a basic scheduler that schedules a new configuration every time new resources become available.
무작위 검색 및 기타 HPO 알고리즘을 구현하려면 새 리소스를 사용할 수 있을 때마다 새 구성을 예약하는 기본 스케줄러만 필요합니다.
위의 코드는 기본적인 스케줄러인 BasicScheduler 클래스를 정의하는 부분입니다. 이 클래스는 하이퍼파라미터 탐색 과정에서 하이퍼파라미터를 제안하고 업데이트하는 역할을 합니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
class BasicScheduler(HPOScheduler):: BasicScheduler 클래스를 정의합니다. 이 클래스는 HPOScheduler 클래스를 상속합니다.
def __init__(self, searcher: HPOSearcher):: BasicScheduler 클래스의 생성자 메서드를 정의합니다. 이 생성자 메서드는 하이퍼파라미터 탐색을 수행하는 searcher 객체를 입력으로 받습니다.
searcher: 하이퍼파라미터 탐색을 담당하는 HPOSearcher 클래스의 객체입니다.
self.save_hyperparameters(): 하이퍼파라미터를 저장하는 메서드입니다. 이 메서드를 호출하여 BasicScheduler 클래스의 하이퍼파라미터를 저장합니다.
def suggest(self) -> dict:: 하이퍼파라미터 제안 메서드를 구현합니다. 이 메서드는 searcher 객체의 sample_configuration 메서드를 호출하여 다음으로 시도할 하이퍼파라미터 구성을 제안합니다.
self.searcher.sample_configuration(): searcher 객체의 sample_configuration 메서드를 호출하여 하이퍼파라미터 구성을 제안합니다.
def update(self, config: dict, error: float, info=None):: 하이퍼파라미터 업데이트 메서드를 구현합니다. 이 메서드는 searcher 객체의 update 메서드를 호출하여 하이퍼파라미터 탐색 알고리즘의 업데이트 동작을 수행합니다.
self.searcher.update(config, error, additional_info=info): searcher 객체의 update 메서드를 호출하여 하이퍼파라미터 구성(config)과 검증 오차(error)를 기반으로 업데이트 동작을 수행합니다. 추가 정보(info)도 함께 전달할 수 있습니다.
BasicScheduler 클래스는 단순한 스케줄러로, searcher 객체의 메서드를 호출하여 하이퍼파라미터를 제안하고 업데이트합니다. 구체적인 하이퍼파라미터 탐색 알고리즘과 스케줄링 전략은 searcher 객체에서 결정됩니다. 이 클래스를 사용하여 기본적인 하이퍼파라미터 탐색을 수행할 수 있습니다.
19.2.3.Tuner
Finally, we need a component that runs the scheduler/searcher and does some book-keeping of the results. The following code implements a sequential execution of the HPO trials that evaluates one training job after the next and will serve as a basic example. We will later useSyne Tunefor more scalable distributed HPO cases.
마지막으로 스케줄러/검색기를 실행하고 결과를 기록하는 구성 요소가 필요합니다. 다음 코드는 다음 훈련 작업을 차례로 평가하는 HPO 시도의 순차적 실행을 구현하며 기본 예제로 사용됩니다. 나중에 더 확장 가능한 분산 HPO 사례를 위해 Syne Tune을 사용할 것입니다.
위의 코드는 하이퍼파라미터(Hyperparameters) 튜닝을 수행하는 HPOTuner 클래스를 정의하는 부분입니다. 이 클래스는 주어진 하이퍼파라미터 탐색 스케줄러(scheduler)와 목표 함수(objective)를 사용하여 하이퍼파라미터 탐색을 실행하고 결과를 기록하는 역할을 합니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
class HPOTuner(d2l.HyperParameters):: HPOTuner 클래스를 정의합니다. 이 클래스는 d2l.HyperParameters 클래스를 상속합니다.
def __init__(self, scheduler: HPOScheduler, objective: callable):: HPOTuner 클래스의 생성자 메서드를 정의합니다. 이 생성자 메서드는 두 개의 매개변수를 입력으로 받습니다.
objective: 목표 함수(callable)입니다. 이 함수는 하이퍼파라미터 구성을 입력으로 받아 검증 오차를 반환하는 함수여야 합니다.
self.save_hyperparameters(): 하이퍼파라미터를 저장하는 메서드입니다. 이 메서드를 호출하여 HPOTuner 클래스의 하이퍼파라미터를 저장합니다.
self.incumbent, self.incumbent_error, self.incumbent_trajectory, self.cumulative_runtime, self.current_runtime, self.records: 하이퍼파라미터 탐색 결과를 저장하기 위한 인스턴스 변수들입니다. 이 변수들은 향후 결과 분석 및 시각화에 사용됩니다.
def run(self, number_of_trials): 하이퍼파라미터 탐색을 실행하는 메서드입니다. 이 메서드는 number_of_trials 만큼의 하이퍼파라미터 탐색 시도를 수행합니다.
먼저 현재 시간을 측정하여 시도의 시작 시간(start_time)을 저장합니다.
scheduler 객체를 사용하여 다음으로 시도할 하이퍼파라미터 구성(config)을 제안합니다.
objective 함수를 사용하여 제안된 하이퍼파라미터 구성에 대한 검증 오차(error)를 계산합니다.
검증 오차를 float 형태로 변환하여 저장합니다.
scheduler 객체를 사용하여 하이퍼파라미터 탐색 알고리즘을 업데이트합니다.
시도의 실행 시간(runtime)을 계산합니다.
bookkeeping 메서드를 호출하여 결과를 기록합니다.
시도별로 제안된 하이퍼파라미터 구성, 검증 오차, 실행 시간을 출력합니다.
HPOTuner 클래스는 주어진 하이퍼파라미터 탐색 스케줄러와 목표 함수를 사용하여 하이퍼파라미터 탐색을 수행하고 결과를 기록하는 역할을 합니다. 탐색된 결과는 인스턴스 변수에 저장되어 이후 분석 및 시각화에 사용됩니다.
19.2.4.Bookkeeping the Performance of HPO Algorithms
With any HPO algorithm, we are mostly interested in the best performing configuration (calledincumbent) and its validation error after a given wall-clock time. This is why we trackruntimeper iteration, which includes both the time to run an evaluation (call ofobjective) and the time to make a decision (call ofscheduler.suggest). In the sequel, we will plotcumulative_runtimeagainstincumbent_trajectoryin order to visualize theany-time performanceof the HPO algorithm defined in terms ofscheduler(andsearcher). This allows us to quantify not only how well the configuration found by an optimizer works, but also how quickly an optimizer is able to find it.
모든 HPO 알고리즘에서 우리는 가장 성능이 좋은 구성(현재라고 함)과 주어진 wall-clock time 이후의 유효성 검사 오류에 주로 관심이 있습니다. 이것이 바로 우리가 평가 실행 시간(목표 호출)과 결정을 내리는 시간(scheduler.suggest 호출)을 모두 포함하는 반복당 런타임을 추적하는 이유입니다. 후속편에서는 스케줄러(및 검색기) 측면에서 정의된 HPO 알고리즘의 언제든지 성능을 시각화하기 위해 incumbent_trajectory에 대해 cumulative_runtime을 플롯합니다. 이를 통해 우리는 옵티마이저가 찾은 구성이 얼마나 잘 작동하는지 뿐만 아니라 옵티마이저가 이를 얼마나 빨리 찾을 수 있는지를 정량화할 수 있습니다.
@d2l.add_to_class(HPOTuner) #@save
def bookkeeping(self, config: dict, error: float, runtime: float):
self.records.append({"config": config, "error": error, "runtime": runtime})
# Check if the last hyperparameter configuration performs better
# than the incumbent
if self.incumbent is None or self.incumbent_error > error:
self.incumbent = config
self.incumbent_error = error
# Add current best observed performance to the optimization trajectory
self.incumbent_trajectory.append(self.incumbent_error)
# Update runtime
self.current_runtime += runtime
self.cumulative_runtime.append(self.current_runtime)
위의 코드는 HPOTuner 클래스에 새로운 메서드인 bookkeeping을 추가하는 부분입니다. bookkeeping 메서드는 하이퍼파라미터 탐색 결과를 기록하고 현재까지의 최적 하이퍼파라미터 구성 및 검증 오차를 관리합니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
@d2l.add_to_class(HPOTuner): bookkeeping 메서드를 HPOTuner 클래스에 추가하는 데코레이터입니다. 이를 통해 bookkeeping 메서드가 HPOTuner 클래스의 일부로 추가됩니다.
def bookkeeping(self, config: dict, error: float, runtime: float):: bookkeeping 메서드를 정의합니다. 이 메서드는 세 개의 매개변수를 입력으로 받습니다.
config: 현재 시도한 하이퍼파라미터 구성(configuration)을 나타내는 딕셔너리입니다.
error: 현재 시도한 하이퍼파라미터 구성에 대한 검증 오차를 나타내는 부동 소수점 숫자(float)입니다.
runtime: 현재 시도한 하이퍼파라미터 탐색 시도의 실행 시간을 나타내는 부동 소수점 숫자(float)입니다.
self.records.append({"config": config, "error": error, "runtime": runtime}): 시도한 하이퍼파라미터 구성(config), 검증 오차(error), 실행 시간(runtime)을 딕셔너리 형태로 묶어 records 리스트에 추가합니다. 이를 통해 각 시도의 결과가 기록됩니다.
if self.incumbent is None or self.incumbent_error > error:: 현재까지의 최적 하이퍼파라미터 구성(incumbent)이 없거나 현재 시도한 하이퍼파라미터 구성의 검증 오차가 현재까지의 최적 검증 오차(incumbent_error)보다 작을 경우, 새로운 최적 하이퍼파라미터 구성으로 업데이트합니다.
self.incumbent_trajectory.append(self.incumbent_error): 최적 검증 오차를 최적화 경로(incumbent_trajectory)에 추가합니다. 이를 통해 최적 검증 오차의 변화를 추적할 수 있습니다.
self.current_runtime += runtime: 현재 시도한 하이퍼파라미터 탐색 시도의 실행 시간을 누적 실행 시간에 추가합니다.
self.cumulative_runtime.append(self.current_runtime): 누적 실행 시간을 cumulative_runtime 리스트에 추가합니다. 이를 통해 누적 실행 시간의 변화를 추적할 수 있습니다.
bookkeeping 메서드는 하이퍼파라미터 탐색 과정에서 발생한 결과를 기록하고 최적 하이퍼파라미터 구성 및 검증 오차를 관리하는 중요한 역할을 합니다. 이를 통해 하이퍼파라미터 탐색의 진행과 결과 분석을 용이하게 할 수 있습니다.
19.2.5.Example: Optimizing the Hyperparameters of a Convolutional Neural Network
We now use our new implementation of random search to optimize thebatch sizeandlearning rateof theLeNetconvolutional neural network fromSection 7.6. We being by defining the objective function, which will once more be validation error.
이제 우리는 섹션 7.6의 LeNet 컨벌루션 신경망의 배치 크기와 학습 속도를 최적화하기 위해 새로운 무작위 검색 구현을 사용합니다. 우리는 다시 한 번 검증 오류가 될 목적 함수를 정의하고 있습니다.
위의 코드는 하이퍼파라미터 튜닝을 위한 목표 함수인 hpo_objective_lenet 함수를 정의하는 부분입니다. 이 함수는 LeNet 모델을 사용하여 Fashion MNIST 데이터셋에 대한 검증 오차를 반환하는 역할을 합니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
def hpo_objective_lenet(learning_rate, batch_size, max_epochs=10):: hpo_objective_lenet 함수를 정의합니다. 이 함수는 세 개의 하이퍼파라미터와 하나의 선택적 매개변수를 입력으로 받습니다.
learning_rate: 학습률을 나타내는 부동 소수점 숫자(float)입니다.
batch_size: 미니배치 크기를 나타내는 정수(int)입니다.
max_epochs: 최대 에포크 수를 나타내는 정수(int)입니다. 기본값은 10입니다.
model = d2l.LeNet(lr=learning_rate, num_classes=10): LeNet 아키텍처를 사용하여 모델을 초기화합니다. 이때 학습률과 클래스 수를 매개변수로 설정합니다.
trainer = d2l.HPOTrainer(max_epochs=max_epochs, num_gpus=1): 하이퍼파라미터 튜닝을 위한 트레이너 객체를 생성합니다. 최대 에포크 수와 GPU 수를 설정합니다.
data = d2l.FashionMNIST(batch_size=batch_size): Fashion MNIST 데이터셋을 불러와서 데이터 객체를 생성합니다. 이때 미니배치 크기를 설정합니다.
model.apply_init([next(iter(data.get_dataloader(True)))[0]], d2l.init_cnn): 모델의 가중치를 초기화합니다. 이때 데이터로부터 첫 번째 미니배치를 추출하여 초기화에 사용합니다.
trainer.fit(model=model, data=data): 트레이너를 사용하여 모델을 학습시킵니다. 모델과 데이터를 입력으로 제공합니다.
validation_error = trainer.validation_error(): 학습된 모델을 검증 데이터에 대해 평가하여 검증 오차를 계산합니다.
return validation_error: 검증 오차를 반환합니다.
이 함수는 주어진 하이퍼파라미터 구성(learning_rate, batch_size, max_epochs)으로 LeNet 모델을 학습하고 검증 오차를 반환합니다. 이 함수는 하이퍼파라미터 탐색에서 목표로 하는 검증 오차를 최소화하기 위해 호출됩니다.
We also need to define the configuration space. Moreover, the first configuration to be evaluated is the default setting used inSection 7.6.
또한 구성 공간을 정의해야 합니다. 또한 평가할 첫 번째 구성은 섹션 7.6에서 사용된 기본 설정입니다.
위의 코드는 하이퍼파라미터 탐색을 위한 하이퍼파라미터 공간(config_space)과 초기 하이퍼파라미터 구성(initial_config)을 정의하는 부분입니다. 각 부분에 대한 설명은 다음과 같습니다:
config_space: 하이퍼파라미터 공간을 정의하는 딕셔너리입니다. 이 딕셔너리에는 탐색할 하이퍼파라미터의 이름과 각 하이퍼파라미터에 대한 확률 분포가 설정됩니다.
"learning_rate": 학습률을 나타내는 하이퍼파라미터입니다. 이 학습률은 로그 균등 분포(stats.loguniform)를 사용하여 1e-2에서 1 사이의 값 중에서 무작위로 선택됩니다.
"batch_size": 미니배치 크기를 나타내는 하이퍼파라미터입니다. 이 미니배치 크기는 균등 분포(stats.randint)를 사용하여 32에서 256 사이의 정수 중에서 무작위로 선택됩니다.
initial_config: 초기 하이퍼파라미터 구성을 정의하는 딕셔너리입니다. 이 딕셔너리에는 하이퍼파라미터의 이름과 초기값이 설정됩니다.
"learning_rate": 학습률의 초기값을 0.1로 설정합니다.
"batch_size": 미니배치 크기의 초기값을 128로 설정합니다.
이렇게 정의된 config_space와 initial_config를 사용하여 하이퍼파라미터 탐색을 수행할 때, 하이퍼파라미터 탐색 공간은 learning_rate와 batch_size 두 가지 하이퍼파라미터를 다루며, 초기 탐색은 initial_config에서 정의한 값으로 시작합니다. 이후 하이퍼파라미터 탐색 알고리즘이 지정된 공간에서 하이퍼파라미터를 무작위로 탐색하고 목표 함수를 최적화하려고 시도합니다.
위의 코드는 하이퍼파라미터 튜닝 프로세스를 설정하고 실행하는 부분입니다. 이 코드는 다음과 같은 주요 단계로 구성됩니다:
searcher = RandomSearcher(config_space, initial_config=initial_config): RandomSearcher 클래스를 사용하여 하이퍼파라미터 탐색기(searcher)를 생성합니다. 이 탐색기는 정의한 하이퍼파라미터 공간(config_space)에서 무작위로 하이퍼파라미터를 샘플링하며, 초기 하이퍼파라미터 구성(initial_config)은 최초 탐색 시도에서 사용됩니다.
scheduler = BasicScheduler(searcher=searcher): BasicScheduler 클래스를 사용하여 스케줄러(scheduler)를 생성합니다. 이 스케줄러는 하이퍼파라미터 탐색기(searcher)를 기반으로 하이퍼파라미터 탐색을 제어하며, 다음에 시도할 하이퍼파라미터 구성을 추천합니다.
tuner = HPOTuner(scheduler=scheduler, objective=hpo_objective_lenet): HPOTuner 클래스를 사용하여 하이퍼파라미터 튜너(tuner)를 생성합니다. 이 튜너는 스케줄러와 목표 함수(objective)를 입력으로 받습니다. 목표 함수는 하이퍼파라미터 탐색 시 목표로 하는 평가 지표(여기서는 검증 오차)를 최소화하기 위해 호출됩니다.
tuner.run(number_of_trials=5): 하이퍼파라미터 탐색을 실행합니다. number_of_trials 매개변수에 지정된 횟수(여기서는 5번)만큼 하이퍼파라미터 탐색을 반복하며, 각 시도에서 목표 함수를 호출하여 검증 오차를 최소화하는 최적의 하이퍼파라미터를 찾습니다.
이렇게 설정된 하이퍼파라미터 탐색 프로세스를 실행하면, 다양한 하이퍼파라미터 조합을 시도하여 모델의 검증 오차를 최적화하려고 노력합니다. 최적의 하이퍼파라미터 구성과 검증 오차의 기록은 tuner 객체에 저장되며, 최종적으로 가장 좋은 하이퍼파라미터 구성을 찾게 됩니다.
==> 여러 구성으로 여러번 run 하기 때문에 시간이 많이 걸림. 아래는 CoLab에서 돌린 결과. 11분 걸
Below we plot the optimization trajectory of the incumbent to get the any-time performance of random search:
아래에서는 무작위 검색의 언제든지 성능을 얻기 위해 기존 기업의 최적화 궤적을 그립니다.
board = d2l.ProgressBoard(xlabel="time", ylabel="error")
for time_stamp, error in zip(
tuner.cumulative_runtime, tuner.incumbent_trajectory
):
board.draw(time_stamp, error, "random search", every_n=1)
위의 코드는 하이퍼파라미터 탐색 과정에서 검증 오차의 변화를 시각화하는 부분입니다. 코드는 다음과 같이 동작합니다:
board = d2l.ProgressBoard(xlabel="time", ylabel="error"): d2l.ProgressBoard 객체를 생성하여 그래프를 초기화합니다. 이 그래프는 시간(time)에 따른 검증 오차(error)의 변화를 시각화합니다. x축은 시간을 나타내고, y축은 검증 오차를 나타냅니다.
for time_stamp, error in zip(tuner.cumulative_runtime, tuner.incumbent_trajectory):: 하이퍼파라미터 튜닝 과정에서 누적된 시간(time_stamp)과 현재까지의 최적 검증 오차(error)를 반복하면서 그래프를 그립니다.
board.draw(time_stamp, error, "random search", every_n=1): 그래프에 데이터를 추가합니다. 시간(time_stamp)과 검증 오차(error)를 전달하고, "random search"라는 레이블을 지정합니다. every_n=1은 모든 데이터 포인트를 표시하도록 지정하는데, 이 값이 높으면 그래프에 표시되는 데이터 포인트의 수가 감소합니다.
이 코드는 하이퍼파라미터 탐색 과정 중에 검증 오차의 변화를 실시간으로 시각화하여 어떻게 하이퍼파라미터 탐색이 진행되고 있는지를 모니터링할 수 있게 합니다. 그래프는 시간에 따른 검증 오차의 추이를 보여주며, 최적의 하이퍼파라미터 구성을 찾는 과정을 시각적으로 이해하는 데 도움을 줍니다.
19.2.6.Comparing HPO Algorithms
Just as with training algorithms or model architectures, it is important to understand how to best compare different HPO algorithms. Each HPO run depends on two major sources of randomness: the random effects of the training process, such as random weight initialization or mini-batch ordering, and the intrinsic randomness of the HPO algorithm itself, such as the random sampling of random search. Hence, when comparing different algorithms, it is crucial to run each experiment several times and report statistics, such as mean or median, across a population of multiple repetitions of an algorithm based on different seeds of the random number generator.
학습 알고리즘이나 모델 아키텍처와 마찬가지로 다양한 HPO 알고리즘을 가장 잘 비교하는 방법을 이해하는 것이 중요합니다. 각 HPO 실행은 무작위성의 두 가지 주요 소스, 즉 무작위 가중치 초기화 또는 미니 배치 순서 지정과 같은 훈련 프로세스의 무작위 효과와 무작위 검색의 무작위 샘플링과 같은 HPO 알고리즘 자체의 본질적인 무작위성에 따라 달라집니다. 따라서 다양한 알고리즘을 비교할 때 각 실험을 여러 번 실행하고 난수 생성기의 다양한 시드를 기반으로 하는 알고리즘의 여러 반복 모집단에 대한 평균 또는 중앙값과 같은 통계를 보고하는 것이 중요합니다.
To illustrate this, we compare random search (seeSection 19.1.2) and Bayesian optimization(Snoeket al., 2012)on tuning the hyperparameters of a feed-forward neural network. Each algorithm was evaluated50times with a different random seed. The solid line indicates the average performance of the incumbent across these50repetitions and the dashed line the standard deviation. We can see that random search and Bayesian optimization perform roughly the same up to ~1000 seconds, but Bayesian optimization can make use of the past observation to identify better configurations and thus quickly outperforms random search afterwards.
이를 설명하기 위해 피드포워드 신경망의 하이퍼파라미터 조정에 대한 무작위 검색(19.1.2절 참조)과 베이지안 최적화(Snoek et al., 2012)를 비교합니다. 각 알고리즘은 서로 다른 무작위 시드를 사용하여 50회 평가되었습니다. 실선은 50회 반복에 걸쳐 재직자의 평균 성과를 나타내고 점선은 표준 편차를 나타냅니다. 무작위 검색과 베이지안 최적화는 최대 1000초까지 거의 동일하게 수행되지만 베이지안 최적화는 과거 관찰을 활용하여 더 나은 구성을 식별할 수 있으므로 나중에 무작위 검색보다 빠르게 성능이 향상된다는 것을 알 수 있습니다.
19.2.7.Summary
This section laid out a simple, yet flexible interface to implement various HPO algorithms that we will look at in this chapter. Similar interfaces can be found in popular open-source HPO frameworks. We also looked at how we can compare HPO algorithms, and potential pitfall one needs to be aware.
이 섹션에서는 이 장에서 살펴볼 다양한 HPO 알고리즘을 구현하기 위한 간단하면서도 유연한 인터페이스를 제시했습니다. 인기 있는 오픈 소스 HPO 프레임워크에서도 유사한 인터페이스를 찾을 수 있습니다. 또한 HPO 알고리즘을 비교할 수 있는 방법과 알아야 할 잠재적인 함정도 살펴보았습니다.
19.2.8.Exercises
The goal of this exercise is to implement the objective function for a slightly more challenging HPO problem, and to run more realistic experiments. We will use the two hidden layer MLPDropoutMLPimplemented inSection 5.6.
Code up the objective function, which should depend on all hyperparameters of the model andbatch_size. Usemax_epochs=50. GPUs do not help here, sonum_gpus=0. Hint: Modifyhpo_objective_lenet.
Choose a sensible search space, wherenum_hiddens_1,num_hiddens_2are integers in[8,1024], and dropout values lie in[0,0.95], whilebatch_sizelies in[16,384]. Provide code forconfig_space, using sensible distributions fromscipy.stats.
Run random search on this example withnumber_of_trials=20and plot the results. Make sure to first evaluate the default configuration ofSection 5.6, which isinitial_config={'num_hiddens_1':256,'num_hiddens_2':256,'dropout_1':0.5,'dropout_2':0.5,'lr':0.1,'batch_size':256}.
In this exercise, you will implement a new searcher (subclass ofHPOSearcher) which makes decisions based on past data. It depends on parametersprobab_local,num_init_random. Itssample_configurationmethod works as follows. For the firstnum_init_randomcalls, do the same asRandomSearcher.sample_configuration. Otherwise, with probability1-probab_local, do the same asRandomSearcher.sample_configuration. Otherwise, pick the configuration which attained the smallest validation error so far, select one of its hyperparameters at random, and sample its value randomly like inRandomSearcher.sample_configuration, but leave all other values the same. Return this configuration, which is identical to the best configuration so far, except in this one hyperparameter.
Code up this newLocalSearcher. Hint: Your searcher requiresconfig_spaceas argument at construction. Feel free to use a member of typeRandomSearcher. You will also have to implement theupdatemethod.
Re-run the experiment from the previous exercise, but using your new searcher instead ofRandomSearcher. Experiment with different values forprobab_local,num_init_random. However, note that a proper comparison between different HPO methods requires repeating experiments several times, and ideally considering a number of benchmark tasks.
As we have seen in the previous chapters, deep neural networks come with a large number of parameters or weights that are learned during training. On top of these, every neural network has additionalhyperparametersthat need to be configured by the user. For example, to ensure that stochastic gradient descent converges to a local optimum of the training loss (seeSection 12), we have to adjust the learning rate and batch size. To avoid overfitting on training datasets, we might have to set regularization parameters, such as weight decay (seeSection 3.7) or dropout (seeSection 5.6). We can define the capacity and inductive bias of the model by setting the number of layers and number of units or filters per layer (i.e., the effective number of weights).
이전 장에서 살펴본 것처럼 심층 신경망에는 훈련 중에 학습되는 수많은 매개변수 또는 가중치가 포함됩니다. 게다가 모든 신경망에는 사용자가 구성해야 하는 추가 하이퍼파라미터가 있습니다. 예를 들어 확률적 경사하강법이 훈련 손실의 국소 최적값으로 수렴되도록 하려면(섹션 12 참조) 학습 속도와 배치 크기를 조정해야 합니다. 훈련 데이터세트에 대한 과적합을 방지하려면 가중치 감소(섹션 3.7 참조) 또는 드롭아웃(섹션 5.6 참조)과 같은 정규화 매개변수를 설정해야 할 수도 있습니다. 레이어 수와 레이어당 단위 또는 필터 수(즉, 유효 가중치 수)를 설정하여 모델의 용량과 유도 편향을 정의할 수 있습니다.
Unfortunately, we cannot simply adjust these hyperparameters by minimizing the training loss, because this would lead to overfitting on the training data. For example, setting regularization parameters, such as dropout or weight decay to zero leads to a small training loss, but might hurt the generalization performance.
불행하게도 훈련 손실을 최소화함으로써 이러한 하이퍼파라미터를 간단히 조정할 수는 없습니다. 왜냐하면 그렇게 하면 훈련 데이터에 과적합이 발생할 수 있기 때문입니다. 예를 들어, 드롭아웃이나 가중치 감소와 같은 정규화 매개변수를 0으로 설정하면 훈련 손실이 약간 발생하지만 일반화 성능이 저하될 수 있습니다.
Without a different form of automation, hyperparameters have to be set manually in a trial-and-error fashion, in what amounts to a time-consuming and difficult part of machine learning workflows. For example, consider training a ResNet (seeSection 8.6) on CIFAR-10, which requires more than 2 hours on an Amazon Elastic Cloud Compute (EC2)g4dn.xlargeinstance. Even just trying ten hyperparameter configurations in sequence, this would already take us roughly one day. To make matters worse, hyperparameters are usually not directly transferable across architectures and datasets(Bardenetet al., 2013,Feureret al., 2022,Wistubaet al., 2018), and need to be re-optimized for every new task. Also, for most hyperparameters, there are no rule-of-thumbs, and expert knowledge is required to find sensible values.
다른 형태의 자동화가 없으면 하이퍼파라미터는 시행착오 방식으로 수동으로 설정해야 하므로 기계 학습 워크플로에서 시간이 많이 걸리고 어려운 부분이 됩니다. 예를 들어, Amazon Elastic Cloud Compute(EC2) g4dn.xlarge 인스턴스에서 2시간 이상 필요한 CIFAR-10의 ResNet(섹션 8.6 참조) 교육을 고려해 보세요. 10개의 하이퍼파라미터 구성을 순차적으로 시도하는 것만으로도 이미 대략 하루가 걸릴 것입니다. 설상가상으로 하이퍼파라미터는 일반적으로 아키텍처와 데이터 세트 간에 직접 전송할 수 없으며(Bardenet et al., 2013, Feurer et al., 2022, Wistuba et al., 2018) 모든 새로운 작업에 대해 다시 최적화해야 합니다. 또한 대부분의 하이퍼파라미터에는 경험 법칙이 없으며, 합리적인 값을 찾기 위해서는 전문 지식이 필요합니다.
Hyperparameter optimization (HPO)algorithms are designed to tackle this problem in a principled and automated fashion(Feurer and Hutter, 2018), by framing it as a global optimization problem. The default objective is the error on a hold-out validation dataset, but could in principle be any other business metric. It can be combined with or constrained by secondary objectives, such as training time, inference time, or model complexity.
하이퍼파라미터 최적화(HPO) 알고리즘은 이 문제를 전역 최적화 문제로 구성하여 원칙적이고 자동화된 방식으로(Feurer and Hutter, 2018) 해결하도록 설계되었습니다. 기본 목표는 홀드아웃 검증 데이터 세트의 오류이지만 원칙적으로 다른 비즈니스 지표일 수도 있습니다. 이는 훈련 시간, 추론 시간 또는 모델 복잡성과 같은 2차 목표와 결합되거나 제한될 수 있습니다.
Recently, hyperparameter optimization has been extended toneural architecture search (NAS)(Elskenet al., 2018,Wistubaet al., 2019), where the goal is to find entirely new neural network architectures. Compared to classical HPO, NAS is even more expensive in terms of computation and requires additional efforts to remain feasible in practice. Both, HPO and NAS can be considered as sub-fields of AutoML(Hutteret al., 2019), which aims to automate the entire ML pipeline.
최근 하이퍼파라미터 최적화는 완전히 새로운 신경망 아키텍처를 찾는 것이 목표인 신경 아키텍처 검색(NAS)(Elsken et al., 2018, Wistuba et al., 2019)으로 확장되었습니다. 기존 HPO에 비해 NAS는 계산 측면에서 훨씬 더 비싸며 실제로 실행 가능성을 유지하려면 추가 노력이 필요합니다. HPO와 NAS는 모두 전체 ML 파이프라인 자동화를 목표로 하는 AutoML(Hutter et al., 2019)의 하위 분야로 간주될 수 있습니다.
In this section we will introduce HPO and show how we can automatically find the best hyperparameters of the logistic regression example introduced inSection 4.5.
이 섹션에서는 HPO를 소개하고 섹션 4.5에 소개된 로지스틱 회귀 예제의 최상의 하이퍼파라미터를 자동으로 찾는 방법을 보여줍니다.
19.1.1.The Optimization Problem
We will start with a simple toy problem: searching for the learning rate of the multi-class logistic regression modelSoftmaxRegressionfromSection 4.5to minimize the validation error on the Fashion MNIST dataset. While other hyperparameters like batch size or number of epochs are also worth tuning, we focus on learning rate alone for simplicity.
간단한 장난감 문제부터 시작하겠습니다. Fashion MNIST 데이터세트의 검증 오류를 최소화하기 위해 섹션 4.5에서 다중 클래스 로지스틱 회귀 모델 SoftmaxRegression의 학습률을 검색하는 것입니다. 배치 크기나 에포크 수와 같은 다른 하이퍼파라미터도 조정할 가치가 있지만 단순화를 위해 학습 속도에만 중점을 둡니다.
import numpy as np
import torch
from scipy import stats
from torch import nn
from d2l import torch as d2l
Before we can run HPO, we first need to define two ingredients: the objective function and the configuration space.
HPO를 실행하기 전에 먼저 목적 함수와 구성 공간이라는 두 가지 구성 요소를 정의해야 합니다.
19.1.1.1.The Objective Function
The performance of a learning algorithm can be seen as a functionf:X→ℝthat maps from the hyperparameter spacex∈Dto the validation loss. For every evaluation off(x), we have to train and validate our machine learning model, which can be time and compute intensive in the case of deep neural networks trained on large datasets. Given our criterionf(x)our goal is to findx⋆∈argminx∈Xf(x).
학습 알고리즘의 성능은 하이퍼파라미터 공간 x∈D에서 검증 손실로 매핑되는 함수 f:X→ℝ로 볼 수 있습니다. f(x)를 평가할 때마다 기계 학습 모델을 훈련하고 검증해야 하는데, 이는 대규모 데이터 세트에 대해 훈련된 심층 신경망의 경우 시간과 계산 집약적일 수 있습니다. 기준 f(x)가 주어지면 우리의 목표는 x⋆∈argminx∈Xf(x)를 찾는 것입니다.
There is no simple way to compute gradients offwith respect tox, because it would require to propagate the gradient through the entire training process. While there is recent work(Franceschiet al., 2017,Maclaurinet al., 2015)to drive HPO by approximate “hypergradients”, none of the existing approaches are competitive with the state-of-the-art yet, and we will not discuss them here. Furthermore, the computational burden of evaluatingfrequires HPO algorithms to approach the global optimum with as few samples as possible.
x에 대한 f의 기울기를 계산하는 간단한 방법은 없습니다. 왜냐하면 전체 훈련 과정을 통해 기울기를 전파해야 하기 때문입니다. 대략적인 "hypergradients"를 통해 HPO를 구동하는 최근 연구(Franceschi et al., 2017, Maclaurin et al., 2015)가 있지만 기존 접근 방식 중 어느 것도 아직 최첨단 기술과 경쟁할 수 없습니다. 여기서는 논의하지 마세요. 더욱이, f를 평가하는 계산 부담으로 인해 HPO 알고리즘은 가능한 적은 샘플을 사용하여 전역 최적에 접근해야 합니다.
The training of neural networks is stochastic (e.g., weights are randomly initialized, mini-batches are randomly sampled), so that our observations will be noisy:y∼f(x)+ϵ, where we usually assume that theϵ∼N(0,σ)observation noise is Gaussian distributed.
신경망의 훈련은 확률론적입니다(예: 가중치는 무작위로 초기화되고, 미니 배치는 무작위로 샘플링됩니다). 따라서 우리의 관측값은 시끄러울 것입니다: y∼f(x)+ϵ, 여기서 우리는 일반적으로 ϵ∼N( 0,σ) 관측 잡음은 가우스 분포입니다.
Faced with all these challenges, we usually try to identify a small set of well performing hyperparameter configurations quickly, instead of hitting the global optima exactly. However, due to large computational demands of most neural networks models, even this can take days or weeks of compute. We will explore inSection 19.4how we can speed-up the optimization process by either distributing the search or using cheaper-to-evaluate approximations of the objective function.
이러한 모든 문제에 직면했을 때 우리는 일반적으로 전체 최적 상태에 정확히 도달하는 대신 성능이 좋은 소수의 하이퍼파라미터 구성 세트를 신속하게 식별하려고 노력합니다. 그러나 대부분의 신경망 모델의 컴퓨팅 요구량이 많기 때문에 이 작업에도 며칠 또는 몇 주가 걸릴 수 있습니다. 우리는 섹션 19.4에서 검색을 분산하거나 목적 함수의 평가 비용이 더 저렴한 근사치를 사용하여 최적화 프로세스의 속도를 높일 수 있는 방법을 탐색할 것입니다.
We begin with a method for computing the validation error of a model.
모델의 검증 오류를 계산하는 방법부터 시작합니다.
class HPOTrainer(d2l.Trainer): #@save
def validation_error(self):
self.model.eval()
accuracy = 0
val_batch_idx = 0
for batch in self.val_dataloader:
with torch.no_grad():
x, y = self.prepare_batch(batch)
y_hat = self.model(x)
accuracy += self.model.accuracy(y_hat, y)
val_batch_idx += 1
return 1 - accuracy / val_batch_idx
위의 코드는 하이퍼파라미터 최적화(Hyperparameter Optimization, HPO)를 수행하는 데 사용되는 HPOTrainer 클래스를 정의하는 파트입니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
class HPOTrainer(d2l.Trainer):: HPOTrainer 클래스를 정의합니다. 이 클래스는 d2l.Trainer 클래스를 상속받아 하이퍼파라미터 최적화를 위한 훈련 기능을 추가합니다.
def validation_error(self):: 검증 데이터셋을 사용하여 모델의 성능을 평가하고 검증 오차를 계산하는 메서드를 정의합니다.
self.model.eval(): 모델을 평가 모드로 설정합니다. 이 모드에서는 모델이 평가되기만 하고 그라디언트 계산이 비활성화됩니다.
accuracy = 0: 정확도를 초기화합니다. 이 변수는 모든 검증 배치의 정확도를 누적하기 위해 사용됩니다.
val_batch_idx = 0: 검증 배치의 인덱스를 초기화합니다.
for batch in self.val_dataloader:: 검증 데이터셋의 배치들을 반복합니다. self.val_dataloader는 검증 데이터셋을 로드하는 데 사용되는 데이터 로더입니다.
with torch.no_grad():: 그라디언트 계산을 비활성화하는 torch.no_grad() 컨텍스트를 생성합니다. 이 컨텍스트 내에서는 모델이 평가될 때 그라디언트가 계산되지 않습니다.
x, y = self.prepare_batch(batch): 현재 배치를 준비하고 입력 데이터 x와 레이블 y를 가져옵니다.
y_hat = self.model(x): 모델을 사용하여 입력 데이터에 대한 예측을 계산합니다.
accuracy += self.model.accuracy(y_hat, y): 현재 배치의 정확도를 계산하여 누적합니다. self.model.accuracy()는 모델의 예측과 실제 레이블을 사용하여 정확도를 계산하는 메서드입니다.
val_batch_idx += 1: 검증 배치 인덱스를 증가시킵니다.
return 1 - accuracy / val_batch_idx: 검증 데이터셋 전체에 대한 정확도를 계산하고, 1에서 빼서 검증 오차를 계산합니다. 이 오차는 하이퍼파라미터 최적화 과정에서 사용될 수 있습니다.
이렇게 정의된 HPOTrainer 클래스는 모델의 성능을 검증 데이터셋을 사용하여 평가하고 검증 오차를 계산하는 기능을 제공합니다. 이 클래스는 하이퍼파라미터 최적화의 일부로 모델의 성능을 측정하는 데 유용하게 사용될 수 있습니다.
We optimize validation error with respect to the hyperparameter configurationconfig, consisting of thelearning_rate. For each evaluation, we train our model formax_epochsepochs, then compute and return its validation error:
learning_rate로 구성된 하이퍼파라미터 구성 config에 대한 검증 오류를 최적화합니다. 각 평가에 대해 max_epochs epoch에 대한 모델을 훈련한 다음 검증 오류를 계산하고 반환합니다.
def hpo_objective_softmax_classification(config, max_epochs=8):
learning_rate = config["learning_rate"]
trainer = d2l.HPOTrainer(max_epochs=max_epochs)
data = d2l.FashionMNIST(batch_size=16)
model = d2l.SoftmaxRegression(num_outputs=10, lr=learning_rate)
trainer.fit(model=model, data=data)
return trainer.validation_error().detach().numpy()
위의 코드는 하이퍼파라미터 최적화(Hyperparameter Optimization, HPO)를 수행하기 위한 목적 함수인 hpo_objective_softmax_classification를 정의하는 파트입니다. 이 함수는 주어진 하이퍼파라미터 구성을 사용하여 소프트맥스 분류 모델을 훈련하고 검증 오차를 반환합니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
def hpo_objective_softmax_classification(config, max_epochs=8):: 하이퍼파라미터 최적화를 위한 목적 함수를 정의합니다. 함수는 두 개의 매개변수를 입력으로 받습니다. config는 하이퍼파라미터 구성을 나타내며, max_epochs는 모델의 최대 훈련 에폭을 지정하는 매개변수로 기본값은 8입니다.
learning_rate = config["learning_rate"]: 주어진 하이퍼파라미터 구성에서 학습률(learning rate)을 가져옵니다. 이는 하이퍼파라미터 최적화의 일부로 조정될 값입니다.
trainer = d2l.HPOTrainer(max_epochs=max_epochs): 하이퍼파라미터 최적화를 위한 d2l.HPOTrainer 객체를 생성합니다. max_epochs는 모델의 최대 훈련 에폭을 설정합니다.
data = d2l.FashionMNIST(batch_size=16): Fashion MNIST 데이터셋을 로드하고 데이터 로더를 생성합니다. 이 데이터는 모델을 훈련하기 위한 학습 및 검증 데이터로 사용됩니다. batch_size는 한 번에 처리할 데이터의 배치 크기를 설정합니다.
model = d2l.SoftmaxRegression(num_outputs=10, lr=learning_rate): 소프트맥스 회귀(Softmax Regression) 모델을 생성합니다. num_outputs는 출력 클래스(레이블)의 수를 설정하며, lr은 학습률을 설정합니다.
trainer.fit(model=model, data=data): trainer를 사용하여 모델을 학습합니다. 모델과 데이터를 전달하고, 지정된 에폭 수(max_epochs)만큼 훈련을 수행합니다.
return trainer.validation_error().detach().numpy(): 훈련된 모델의 검증 오차를 계산하고 반환합니다. 검증 오차는 trainer.validation_error()를 통해 얻으며, NumPy 배열로 변환하여 반환합니다.
이 함수는 주어진 하이퍼파라미터 구성을 사용하여 모델을 훈련하고 검증 오차를 반환하므로, 하이퍼파라미터 최적화 알고리즘(예: Bayesian Optimization)에 의해 최적의 하이퍼파라미터 구성을 찾는 데 사용됩니다.
19.1.1.2.The Configuration Space
Along with the objective functionf(x), we also need to define the feasible setx∈Xto optimize over, known asconfiguration spaceorsearch space. For our logistic regression example, we will use:
목적 함수 objective function f(x)와 함께 구성 공간 configuration space 또는 검색 공간 search space 으로 알려진 최적화를 위한 실행 가능한 집합 x∈X도 정의해야 합니다. 로지스틱 회귀 예제에서는 다음을 사용합니다.
위의 코드는 하이퍼파라미터 최적화를 위한 하이퍼파라미터 공간을 정의하는 부분입니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
config_space = {"learning_rate": stats.loguniform(1e-4, 1)}: config_space라는 딕셔너리를 정의합니다. 이 딕셔너리는 하이퍼파라미터 공간을 설명하는 엔트리(항목)를 포함합니다.
"learning_rate": 이 항목은 하이퍼파라미터의 이름인 "learning_rate"를 나타냅니다. 이 이름은 해당 하이퍼파라미터를 식별하는 데 사용됩니다.
stats.loguniform(1e-4, 1): "learning_rate" 하이퍼파라미터의 값 범위를 지정합니다. stats.loguniform은 로그 스케일로 분포하는 값을 생성하는 함수입니다. 여기서는 1e-4에서부터 1까지의 로그 스케일 분포를 정의하고 있으므로, "learning_rate" 하이퍼파라미터의 값은 1e-4에서부터 1 사이의 값 중 하나가 될 것입니다.
이 코드는 하이퍼파라미터 최적화 과정에서 어떤 하이퍼파라미터를 탐색할 것인지를 정의하는데 사용됩니다. 여기서는 "learning_rate"라는 하이퍼파라미터의 값 범위를 로그 스케일로 지정하고 있으므로, 최적의 학습률을 찾기 위해 로그 스케일로 값을 탐색할 수 있습니다.
Here we use the use theloguniformobject from SciPy, which represents a uniform distribution between -4 and -1 in the logarithmic space. This object allows us to sample random variables from this distribution.
여기서는 로그 공간에서 -4와 -1 사이의 균일 분포를 나타내는 SciPy의 loguniform 객체를 사용합니다. 이 객체를 사용하면 이 분포에서 무작위 변수를 샘플링할 수 있습니다.
Each hyperparameter has a data type, such asfloatforlearning_rate, as well as a closed bounded range (i.e., lower and upper bounds). We usually assign a prior distribution (e.g, uniform or log-uniform) to each hyperparameter to sample from. Some positive parameters, such aslearning_rate, are best represented on a logarithmic scale as optimal values can differ by several orders of magnitude, while others, such as momentum, come with linear scale.
각 하이퍼파라미터에는 learning_rate의 float와 같은 데이터 유형과 닫힌 경계 범위(예: 하한 및 상한)가 있습니다. 우리는 일반적으로 샘플링할 각 하이퍼파라미터에 사전 분포(예: 균일 또는 로그 균일)를 할당합니다. learning_rate와 같은 일부 양수 매개변수는 최적의 값이 여러 차수만큼 다를 수 있으므로 로그 척도로 가장 잘 표현되는 반면, 모멘텀과 같은 다른 매개변수는 선형 척도로 제공됩니다.
Below we show a simple example of a configuration space consisting of typical hyperparameters of a multi-layer perceptron including their type and standard ranges.
아래에서는 유형 및 표준 범위를 포함하여 다층 퍼셉트론의 일반적인 하이퍼 매개변수로 구성된 구성 공간의 간단한 예를 보여줍니다.
: Example configuration space of multi-layer perceptron
In general, the structure of the configuration spaceXcan be complex and it can be quite different fromℝ**d. In practice, some hyperparameters may depend on the value of others. For example, assume we try to tune the number of layers for a multi-layer perceptron, and for each layer the number of units. The number of units of thel-thlayer is relevant only if the network has at leastl+1layers. These advanced HPO problems are beyond the scope of this chapter. We refer the interested reader to(Baptista and Poloczek, 2018,Hutteret al., 2011,Jenattonet al., 2017).
일반적으로 구성 공간 X의 구조는 복잡할 수 있으며 ℝ**d와 상당히 다를 수 있습니다. 실제로 일부 하이퍼파라미터는 다른 하이퍼파라미터의 값에 따라 달라질 수 있습니다. 예를 들어, 다층 퍼셉트론의 레이어 수와 각 레이어의 단위 수를 조정하려고 한다고 가정합니다. l번째 레이어의 단위 수는 네트워크에 l+1개 이상의 레이어가 있는 경우에만 관련이 있습니다. 이러한 고급 HPO 문제는 이 장의 범위를 벗어납니다. 관심 있는 독자에게는 (Baptista and Poloczek, 2018, Hutter et al., 2011, Jenatton et al., 2017)을 참조하시기 바랍니다.
The configuration space plays an important role for hyperparameter optimization, since no algorithms can find something that is not included in the configuration space. On the other hand, if the ranges are too large, the computation budget to find well performing configurations might become infeasible.
구성 공간은 구성 공간에 포함되지 않은 것을 어떤 알고리즘도 찾을 수 없기 때문에 하이퍼파라미터 최적화에 중요한 역할을 합니다. 반면에 범위가 너무 크면 성능이 좋은 구성을 찾기 위한 계산 예산이 실행 불가능해질 수 있습니다.
19.1.2.Random Search
Random searchis the first hyperparameter optimization algorithm we will consider. The main idea of random search is to independently sample from the configuration space until a predefined budget (e.g maximum number of iterations) is exhausted, and to return the best observed configuration. All evaluations can be executed independently in parallel (seeSection 19.3), but here we use a sequential loop for simplicity.
무작위 검색은 우리가 고려할 첫 번째 하이퍼파라미터 최적화 알고리즘입니다. 무작위 검색의 주요 아이디어는 미리 정의된 예산(예: 최대 반복 횟수)이 소진될 때까지 구성 공간에서 독립적으로 샘플링하고 가장 잘 관찰된 구성을 반환하는 것입니다. 모든 평가는 독립적으로 병렬로 실행될 수 있지만(19.3절 참조) 여기서는 단순화를 위해 순차 루프를 사용합니다.
errors, values = [], []
num_iterations = 5
for i in range(num_iterations):
learning_rate = config_space["learning_rate"].rvs()
print(f"Trial {i}: learning_rate = {learning_rate}")
y = hpo_objective_softmax_classification({"learning_rate": learning_rate})
print(f" validation_error = {y}")
values.append(learning_rate)
errors.append(y)
위의 코드는 하이퍼파라미터 최적화 과정을 반복적으로 실행하고, 각 반복에서 얻은 검증 오차와 하이퍼파라미터 값을 기록하는 부분입니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
errors, values = [], []: 검증 오차와 하이퍼파라미터 값을 저장할 빈 리스트 errors와 values를 초기화합니다.
num_iterations = 5: 하이퍼파라미터 최적화를 몇 번 반복할지를 나타내는 변수를 설정합니다. 여기서는 5번 반복합니다.
for i in range(num_iterations):: num_iterations 횟수만큼 반복하는 루프를 시작합니다.
learning_rate = config_space["learning_rate"].rvs(): 하이퍼파라미터 공간에서 "learning_rate" 하이퍼파라미터의 값을 랜덤하게 선택합니다. 이렇게 선택된 학습률 값을 learning_rate 변수에 저장합니다.
print(f"Trial {i}: learning_rate = {learning_rate}"): 현재 반복의 정보를 출력합니다. 이 부분은 각 반복에서 어떤 하이퍼파라미터 값이 선택되었는지 확인하는 데 사용됩니다.
y = hpo_objective_softmax_classification({"learning_rate": learning_rate}): 선택된 하이퍼파라미터 값을 사용하여 hpo_objective_softmax_classification 함수를 호출하여 검증 오차를 계산합니다. 계산된 검증 오차는 y에 저장됩니다.
print(f" validation_error = {y}"): 계산된 검증 오차를 출력합니다. 이 부분은 각 반복에서 얻은 검증 오차를 확인하는 데 사용됩니다.
values.append(learning_rate): 선택된 하이퍼파라미터 값(학습률)을 values 리스트에 추가합니다. 이렇게 하이퍼파라미터 값들이 기록됩니다.
위의 코드는 하이퍼파라미터 최적화 실험 결과에서 최적의 학습률(learning rate)을 선택하는 부분입니다. 코드의 목적과 각 부분에 대한 설명은 다음과 같습니다:
best_idx = np.argmin(errors): 검증 오차(errors 리스트) 중에서 가장 작은 값을 가지는 인덱스를 찾습니다. np.argmin() 함수는 배열에서 최솟값을 가지는 원소의 인덱스를 반환합니다. 이를 통해 최적의 학습률을 선택할 때 해당 인덱스를 사용할 것입니다.
print(f"optimal learning rate = {values[best_idx]}"): 최적의 학습률을 출력합니다. values 리스트에서 best_idx 인덱스에 해당하는 학습률 값을 가져와서 출력합니다. 이 값은 검증 오차가 가장 작을 때의 학습률을 나타냅니다.
즉, 이 코드는 여러 번의 하이퍼파라미터 최적화 실험을 통해 얻은 검증 오차를 분석하여, 검증 오차가 가장 작은 학습률을 최적의 학습률로 선택하고 출력합니다. 이렇게 찾은 최적의 하이퍼파라미터 값을 모델 학습에 사용할 수 있습니다.
optimal learning rate = 0.09844872561810249
Due to its simplicity and generality, random search is one of the most frequently used HPO algorithms. It does not require any sophisticated implementation and can be applied to any configuration space as long as we can define some probability distribution for each hyperparameter.
단순성과 일반성으로 인해 무작위 검색은 가장 자주 사용되는 HPO 알고리즘 중 하나입니다. 정교한 구현이 필요하지 않으며 각 하이퍼파라미터에 대한 확률 분포를 정의할 수 있는 한 모든 구성 공간에 적용할 수 있습니다.
Unfortunately random search also comes with a few shortcomings. First, it does not adapt the sampling distribution based on the previous observations it collected so far. Hence, it is equally likely to sample a poorly performing configuration than a better performing configuration. Second, the same amount of resources are spent for all configurations, even though some may show poor initial performance and are less likely to outperform previously seen configurations.
불행하게도 무작위 검색에는 몇 가지 단점도 있습니다. 첫째, 지금까지 수집한 이전 관측치를 기반으로 샘플링 분포를 조정하지 않습니다. 따라서 성능이 더 좋은 구성보다 성능이 낮은 구성을 샘플링할 가능성이 동일합니다. 둘째, 일부 구성은 초기 성능이 좋지 않고 이전 구성보다 성능이 떨어질 가능성이 있더라도 모든 구성에 동일한 양의 리소스가 사용됩니다.
In the next sections we will look at more sample efficient hyperparameter optimization algorithms that overcome the shortcomings of random search by using a model to guide the search. We will also look at algorithms that automatically stop the evaluation process of poorly performing configurations to speed up the optimization process.
다음 섹션에서는 검색을 안내하는 모델을 사용하여 무작위 검색의 단점을 극복하는 더 효율적인 하이퍼파라미터 최적화 알고리즘 샘플을 살펴보겠습니다. 또한 최적화 프로세스 속도를 높이기 위해 성능이 낮은 구성의 평가 프로세스를 자동으로 중지하는 알고리즘도 살펴보겠습니다.
19.1.3.Summary
In this section we introduced hyperparameter optimization (HPO) and how we can phrase it as a global optimization by defining a configuration space and an objective function. We also implemented our first HPO algorithm, random search, and applied it on a simple softmax classification problem.
이 섹션에서는 하이퍼파라미터 최적화(HPO)를 소개하고 구성 공간과 목적 함수를 정의하여 이를 전역 최적화로 표현하는 방법을 소개했습니다. 또한 첫 번째 HPO 알고리즘인 무작위 검색을 구현하고 이를 간단한 소프트맥스 분류 문제에 적용했습니다.
While random search is very simple, it is the better alternative to grid search, which simply evaluates a fixed set of hyperparameters. Random search somewhat mitigates the curse of dimensionality(Bellman, 1966), and can be far more efficient than grid search if the criterion most strongly depends on a small subset of the hyperparameters.
무작위 검색은 매우 간단하지만, 단순히 고정된 하이퍼파라미터 세트를 평가하는 그리드 검색보다 더 나은 대안입니다. 무작위 검색은 차원의 저주를 어느 정도 완화하며(Bellman, 1966), 기준이 하이퍼 매개변수의 작은 하위 집합에 가장 크게 의존하는 경우 그리드 검색보다 훨씬 더 효율적일 수 있습니다.
19.1.4.Exercises
In this chapter, we optimize the validation error of a model after training on a disjoint training set. For simplicity, our code usesTrainer.val_dataloader, which maps to a loader aroundFashionMNIST.val.
Convince yourself (by looking at the code) that this means we use the original FashionMNIST training set (60000 examples) for training, and the originaltest set(10000 examples) for validation.
Why could this practice be problematic? Hint: Re-readSection 3.6, especially aboutmodel selection.
What should we have done instead?
We stated above that hyperparameter optimization by gradient descent is very hard to do. Consider a small problem, such as training a two-layer perceptron on the FashionMNIST dataset (Section 5.2) with a batch size of 256. We would like to tune the learning rate of SGD in order to minimize a validation metric after one epoch of training.
Why cannot we use validationerrorfor this purpose? What metric on the validation set would you use?
Sketch (roughly) the computational graph of the validation metric after training for one epoch. You may assume that initial weights and hyperparameters (such as learning rate) are input nodes to this graph. Hint: Re-read about computational graphs inSection 5.3.
Give a rough estimate of the number of floating point values you need to store during a forward pass on this graph. Hint: FashionMNIST has 60000 cases. Assume the required memory is dominated by the activations after each layer, and look up the layer widths inSection 5.2.
Apart from the sheer amount of compute and storage required, what other issues would gradient-based hyperparameter optimization run into? Hint: Re-read about vanishing and exploding gradients inSection 5.4.
Advanced: Read(Maclaurinet al., 2015)for an elegant (yet still somewhat unpractical) approach to gradient-based HPO.
Grid search is another HPO baseline, where we define an equi-spaced grid for each hyperparameter, then iterate over the (combinatorial) Cartesian product in order to suggest configurations.
We stated above that random search can be much more efficient than grid search for HPO on a sizable number of hyperparameters, if the criterion most strongly depends on a small subset of the hyperparameters. Why is this? Hint: Read(Bergstraet al., 2011).
The performance of every machine learning model depends on its hyperparameters. They control the learning algorithm or the structure of the underlying statistical model. However, there is no general way to choose hyperparameters in practice. Instead, hyperparameters are often set in a trial-and-error manner or sometimes left to their default values by practitioners, leading to suboptimal generalization.
모든 기계 학습 모델의 성능은 하이퍼파라미터에 따라 달라집니다. 학습 알고리즘이나 기본 통계 모델의 구조를 제어합니다. 그러나 실제로 하이퍼파라미터를 선택하는 일반적인 방법은 없습니다. 대신 초매개변수는 종종 시행착오 방식으로 설정되거나 때로는 실무자가 기본값으로 남겨두어 차선책 일반화로 이어집니다.
Hyperparameter optimization provides a systematic approach to this problem, by casting it as an optimization problem: a good set of hyperparameters should (at least) minimize a validation error. Compared to most other optimization problems arising in machine learning, hyperparameter optimization is a nested one, where each iteration requires training and validating a machine learning model.
하이퍼파라미터 최적화는 이 문제를 최적화 문제로 캐스팅하여 이 문제에 대한 체계적인 접근 방식을 제공합니다. 좋은 하이퍼파라미터 세트는 (적어도) 유효성 검사 오류를 최소화해야 합니다. 기계 학습에서 발생하는 대부분의 다른 최적화 문제와 비교할 때 하이퍼파라미터 최적화는 중첩된 문제로, 각 반복에는 기계 학습 모델을 훈련하고 검증해야 합니다.
In this chapter, we will first introduce the basics of hyperparameter optimization. We will also present some recent advancements that improve the overall efficiency of hyperparameter optimization by exploiting cheap-to-evaluate proxies of the original objective function. At the end of this chapter, you should be able to apply state-of-the-art hyperparameter optimization techniques to optimize the hyperparameter of your own machine learning algorithm.
이번 장에서는 먼저 하이퍼파라미터 최적화의 기본 사항을 소개합니다. 또한 원래 목적 함수의 평가하기 쉬운 프록시를 활용하여 하이퍼파라미터 최적화의 전반적인 효율성을 향상시키는 몇 가지 최근 발전 사항을 제시할 것입니다. 이 장을 마치면 최첨단 하이퍼파라미터 최적화 기술을 적용하여 자체 머신러닝 알고리즘의 하이퍼파라미터를 최적화할 수 있게 됩니다.
Hyperparameter Optimization 이란? (GPT 답변)
**하이퍼파라미터 최적화(Hyperparameter Optimization, HPO)**는 기계 학습 모델의 성능을 최대화하기 위해 모델의 하이퍼파라미터를 선택하는 과정을 가리킵니다. 하이퍼파라미터는 모델 자체가 아니라 모델을 구성하거나 교육하는 동안 조정할 수 있는 매개변수입니다. 이러한 하이퍼파라미터를 올바르게 선택하면 모델의 성능이 향상되며, 과적합(overfitting) 또는 과소적합(underfitting)과 같은 문제를 방지할 수 있습니다.
하이퍼파라미터 최적화 과정은 다음 단계로 진행됩니다:
하이퍼파라미터 공간 정의: 먼저 어떤 하이퍼파라미터를 조정할지 결정하고, 각 하이퍼파라미터의 유효한 범위 또는 가능한 값의 집합을 정의합니다. 예를 들어, 학습률(learning rate), 배치 크기(batch size), 은닉층의 수, 정규화 가중치 등의 하이퍼파라미터를 선택합니다.
목적 함수 정의: 하이퍼파라미터 최적화의 목표는 모델의 성능을 최대화하는 것이므로, 성능을 측정할 목적 함수(또는 손실 함수)를 정의해야 합니다. 이 목적 함수는 하이퍼파라미터 설정에 대한 모델의 성능을 측정합니다.
최적화 알고리즘 선택: 하이퍼파라미터 최적화를 위해 사용할 최적화 알고리즘을 선택합니다. 일반적으로 랜덤 서치(Random Search), 베이지안 최적화(Bayesian Optimization), 유전 알고리즘(Genetic Algorithm) 등의 다양한 알고리즘이 사용됩니다.
하이퍼파라미터 최적화 실행: 선택한 최적화 알고리즘을 사용하여 목적 함수를 최대화하는(또는 최소화하는) 하이퍼파라미터 조합을 찾습니다. 이러한 최적화 과정은 목적 함수를 여러 번 평가하며 진행됩니다.
최적의 하이퍼파라미터 설정 도출: 최적화 과정이 완료되면 최적의 하이퍼파라미터 설정을 얻게 됩니다. 이 설정을 사용하여 모델을 훈련하고 성능을 확인합니다.
하이퍼파라미터 최적화는 기계 학습 모델의 품질을 향상시키고, 모델을 더 효과적으로 조정하며, 과적합과 같은 문제를 방지하는 데 중요한 역할을 합니다. 그러나 주의할 점은 최적화 과정 자체가 계산적으로 비용이 많이 들 수 있으며, 하이퍼파라미터 공간이 크고 목적 함수가 불연속 또는 미분 불가능한 경우에 도전적일 수 있습니다. 따라서 하이퍼파라미터 최적화를 수행할 때는 최적화 알고리즘 선택, 하이퍼파라미터 공간 정의 등을 신중하게 고려해야 합니다.