이번에는 PyTorch를 이용하여 DNN 구조를 구현하는 것을 데이터를 불러오는 것부터 하나하나 실습해 보도록 할 것이다.
0. Environmet setting
나는 Python 3.9.0, Cuda=11.7, PyTorch=2.0.0 환경에서 실습을 진행하였다.
1. 사전세팅
우선 실습을 진행하기 전 다양한 라이브러리들을 미리 import하고 추후 모델 재현성을 위해서 모든 seed를 고정시켜 줬다. 저 시드 고정하는 코드는 템플릿으로 앞으로 사용해도 좋을 것 같다.
1-1. Library
import matplotlib.pyplot as plt
import torch # PyTorch 라이브러리
import torch.nn as nn # 모델 구성을 위한 라이브러리
from torch.utils.data import DataLoader # optimizer 설정을 위한 라이브러리
import torchvision # PyTorch의 컴퓨터 비전 라이브러리
import torchvision.transforms as T # 이미지 변환을 위한 모듈
import torchvision.utils as vutils # 이미지를 쉽게 처리하기 위한 유틸리티 모듈
1-2. Set seed
# seed 고정
import random
import torch.backends.cudnn as cudnn
def random_seed(seed_num):
torch.manual_seed(seed_num)
torch.cuda.manual_seed(seed_num)
torch.cuda.manual_seed_all(seed_num)
cudnn.benchmark = False
cudnn.deterministic = True
random.seed(seed_num)
random_seed(42)
2. Data set
2-1. Data Download
이번 실습에서는 Mnist 데이터셋을 이용할 것이며, 잘 알겠지만 숫자 0~9까지의 손글씨 이미지가 포함된 데이터셋으로 총 6만 개의 train data와 1만 개의 test data로 이루어져 있으며 이미지와 각 라벨로 이루어져 있다.
`torchvision.datasets`에 다양한 비전 관련 dataset이 포함되어 있어서 쉽게 다운로드 받을 수 있었다.
사용방법은 `torchvision.datasets`뒤에 불로오고자 하는 데이터셋을 입력하고 파라미터를 입력해줘야 한다.
파라미터는 `root`는 다운로드 경로, `transform`은 데이터 선처리, `train`은 train이면 True/ test면 False로,`download`는 이미 다운로드되어있다면 True 아니라면 False를 넣어준다. 아래 코드에서는 선처리로 데이터를 Tensor형태로 변경해 주고, train과 test 데이터를 각각 불러왔다.
# 데이터를 불러올 때, 필요한 변환(transform)을 정의합니다.
mnist_transform = T.Compose([T.ToTensor(),])
# torchvision 라이브러리를 사용하여 MNIST Dataset을 불러옵니다.
download_root = './MNIST_DATASET'
train_dataset = torchvision.datasets.MNIST(download_root, transform=mnist_transform, train=True, download=True) # train dataset 다운로드
test_dataset = torchvision.datasets.MNIST(download_root, transform=mnist_transform, train=False, download=True) # test dataset 다운로드
데이터 셋을불러온 후, train set을 이용하여 valid set을 생성해 줬다.
2-2. Data Split
# 데이터 셋을 학습 데이터 셋과 검증 데이터 셋으로 분리합니다.
total_size = len(train_dataset)
train_num, valid_num = int(total_size * 0.8), int(total_size * 0.2) # 8 : 2 = train : valid
print("Train dataset 개수 : ",train_num)
print("Validation dataset 개수 : ",valid_num)
train_dataset, valid_dataset = torch.utils.data.random_split(train_dataset, [train_num, valid_num]) # train - valid set 나누기
머신러닝을 위한 데이터를 분할할 때는 numpy나 dataframe 형태로 되어있어서 scikit learn의 `train_test_split()`을 사용했지만 torch를 다룰 때는 `torch.utils.data.random_split()`을 이용하여 데이터를 분할한다.
2-3. Data Load
이제 데이터를 load할건데, torchvision.datasets를 이용하여 불러온 데이터를 DataLoader의 인자로 넘겨줘야 한다.
또한 `batch_size`: 미니 배치의 크기, `shuffle`:epoch마다 데이터가 섞을지 안 섞을지, `num_workers`:데이터로드에 사용되는 서브 프로세스 개수, `drop_last`:마지막 배치의 데이터 크기가 미니 배치보다 작을 경우 버릴지를 선택, 등등 다양한 추가적인 파라미터를 사용할 수 있다.
batch_size = 32
train_dataloader = DataLoader(train_dataset, batch_size = batch_size, shuffle = True)
valid_dataloader = DataLoader(valid_dataset, batch_size = batch_size, shuffle = False)
test_dataloader = DataLoader(test_dataset, batch_size = batch_size, shuffle = False)
2-4. Data Visualize
마지막으로 불러온 데이터를 확인해보면 다음과 같다. `from torchvision.utils import make_grid`의 `vutils.make_grid`를 이용하여 쉽게 시각화할 수 있다.
grid = vutils.make_grid(images, nrow=8)
plt.figure(figsize=(12,12))
plt.imshow(grid.numpy().transpose((1,2,0)))
plt.title("mini batch visualization")
plt.axis('off')
plt.show()
3. Model- DNN
일반적인 이미지데이터를 다루는 딥러닝 모델은 cnn구조를 이용하는데, 이번 실습에서는 우선 DNN을 이용한 모델을 만들어보고 다음으로 CNN을 이용하여 구현해 보도록 하겠다.
3-1. 모델 구조
DNN 모델에 input을 넣을 때는 모두 flatten을 이용해 펼쳐서 넣어줘야한다.
모델은 위 그림과 같이 3개의 fully connected layer로 구성하고 최종적으로 분류를 위한 fully connected layer을 하고 softmax layer을 거쳐서 최종결과가 도출된다.
또한 이전에 성능고도화에서 배운 것을 이용하여 `Batch normalization`을 진행해 줄 것이며, 과적합 방지를 위해 `Drop out`도 이용할 것이다. Batch normalization과 Drop out은 필수가 아니므로 실험에 따라 선택하여 변경할 수 있도록 구성하였다. 이때 해당 파라미터를 False로 지정하면 그냥 통과시키는 `nn.Identity`블록을 쌓는다.
self.fc1 = nn.Linear(28 * 28, hidden_dim * 4)
self.fc2 = nn.Linear(hidden_dim * 4, hidden_dim * 2)
self.fc3 = nn.Linear(hidden_dim * 2, hidden_dim)
self.classifier = nn.Linear(hidden_dim, num_classes)
# Batch normalization: apply_batchnorm 파라미터가 False일 경우 그냥 통과
self.batchnorm1 = nn.BatchNorm1d(hidden_dim * 4) if apply_batchnorm else nn.Identity()
self.batchnorm2 = nn.BatchNorm1d(hidden_dim * 2) if apply_batchnorm else nn.Identity()
self.batchnorm3 = nn.BatchNorm1d(hidden_dim) if apply_batchnorm else nn.Identity()
# Dropout: apply_dropout 파라미터가 False일 경우 그냥 통과
self.dropout1 = nn.Dropout(dropout_ratio) if apply_dropout else nn.Identity()
self.dropout2 = nn.Dropout(dropout_ratio) if apply_dropout else nn.Identity()
self.dropout3 = nn.Dropout(dropout_ratio) if apply_dropout else nn.Identity()
# Activation function: apply_activation 파라미터가 False일 경우 그냥 통과
self.activation1 = nn.ReLU() if apply_activation else nn.Identity()
self.activation2 = nn.ReLU() if apply_activation else nn.Identity()
self.activation3 = nn.ReLU() if apply_activation else nn.Identity()
self.softmax = nn.LogSoftmax(dim = 1)
다음으로 모델의 순전파 과정을 작성해 준다.
def forward(self, x):
"""
Input: [batch_size, 1, 28, 28] / Output: [batch_size, num_classes]
"""
# 28*28의 이미지를 flatten 시켜줌.
# [batch_size, 784]
x = x.view(x.shape[0], -1)
x = self.fc1(x) # [batch_size, dim * 4]
x = self.batchnorm1(x)
x = self.activation1(x)
x = self.dropout1(x)
x = self.fc2(x) # [batch_size, dim * 2]
x = self.batchnorm2(x)
x = self.activation2(x)
x = self.dropout2(x)
x = self.fc3(x) # [batch_size, dim]
x = self.batchnorm3(x)
x = self.activation3(x)
x = self.dropout3(x)
x = self.classifier(x) # [batch_size, 10]
output = self.softmax(x)
return output
위와 같이 구성하게 될 경우 layer을 하나 추가할 때마다 여러 블록들을 일일이 추가해줘야 하기 때문에 보통 저런 식으로 짜지 않고 반복문을 이용하여 쉽게 레이어를 추가할 수 있도록 구현한다.
class DNN(nn.Module):
def __init__(self, hidden_dims, num_classes, dropout_ratio, apply_batchnorm, apply_dropout, apply_activation, set_super):
if set_super:
super().__init__()
self.hidden_dims = hidden_dims
self.layers = nn.ModuleList()
for i in range(len(self.hidden_dims) - 1):
self.layers.append(nn.Linear(self.hidden_dims[i], self.hidden_dims[i+1]))
if apply_batchnorm:
self.layers.append(nn.BatchNorm1d(self.hidden_dims[i+1]))
if apply_activation:
self.layers.append(nn.ReLU())
if apply_dropout:
self.layers.append(nn.Dropout(dropout_ratio))
self.classifier = nn.Linear(self.hidden_dims[-1], num_classes)
self.softmax = nn.LogSoftmax(dim = 1)
def forward(self, x):
x = x.view(x.shape[0], -1) # [batch_size, 784]
for layer in self.layers:
x = layer(x)
x = self.classifier(x) # [batch_size, 10]
output = self.softmax(x) # [batch_size, 10]
return output
위와 같이 모델구조를 선언한 뒤 해당 모델을 불러와보도록 하겠다.
hidden_dim = 128
hidden_dims = [784, hidden_dim * 4, hidden_dim * 2, hidden_dim]
model = DNN(hidden_dims = hidden_dims, num_classes = 10, dropout_ratio = 0.2, apply_batchnorm = True, apply_dropout = True, apply_activation = True, set_super = True)
output = model(torch.randn((32, 1, 28, 28)))
우리가 선언한 파라미터들 hidden_dim, 분류 class, drop out, batch normalization, 활성화함수 등등을 모두 넣어주고 우리가 사용하고자 할 데이터와 동일한 크기의 random tensor을 생성하여 오류가 나지 않는지 확인해 준다.
위의 모델을 선언할 때 `super().__init__()`이라는 것을 사용했는데, 이는 부모 클래스 DNN 모델을 선언할 때 인자로 들어가는 nn.Module을 초기화하는 코드로 커스텀 모델에서 nn.Module의 `nn.Linear`, `nn.BatchNorm1d` 등을 사용하기 위해서는 필수로 넣어줘야 한다.
3-2. 가중치 초기화
이것도 성능고도화에서 배운 가중치 초기화방법으로, 가중치 초기화를 적절하게 하지 않을 경우 전역해를 제대로 찾지 못하거나 학습속도가 매우 오래 걸릴 수도 있기에 다양한 가중치 초기화 방법을 실험을 통해 정해볼 수 있겠다.
# 모델의 가중치를 초기화하는 함수
def weight_initialization(model, weight_init_method):
for m in model.modules():
if isinstance(m, nn.Linear):
if weight_init_method == 'gaussian':
nn.init.normal_(m.weight)
elif weight_init_method == 'xavier':
nn.init.xavier_normal_(m.weight)
elif weight_init_method == 'kaiming':
nn.init.kaiming_normal_(m.weight)
elif weight_init_method == 'zeros':
nn.init.zeros_(m.weight)
nn.init.zeros_(m.bias)
return model
init_method = 'gaussian' # gaussian, xavier, kaiming, zeros
model.weight_initialization(init_method)
3-3. Training
모델을 선언하였으니 이제 학습과정을 구현해 보도록 하겠다.
3-3-1. 손실함수
모델이 학습하기 위해서는 손실함수를 정의해야 한다. `torch.nn`에 다양한 손실함수들이 구현되어 있으며, 직접 손실함수를 구현해 볼 수도 있다. 본 실습에서는 이미지 데이터 분류를 위해 `torch.nn.NLLLoss`를 사용할 것이다.
우선 보통 분류 문제에서는 `CrossEntropyLoss`를 사용하는데 torch의 `CrossEntropyLoss`는 `LogSoftmax+NLLLoss`로 구현되어 있다. 즉 모델을 선언할 때 softmax를 쓰지 않으면 CrossEntropyLoss를 쓰면 되고, softmax를 썼다면 NLLLoss를 사용하면 된다.
criterion = nn.NLLLoss()
3-3-2. 최적화알고리즘
다음으로 optimizer도 torch에 미리 정의되어 있기 때문에 적절한 알고리즘을 선택해 주면 된다.
본 실습에서는 가장 일반적으로 사용되는 `optim.Adam`을 사용할 것이다.
lr = 0.001
hidden_dim = 128
hidden_dims = [784, hidden_dim * 4, hidden_dim * 2, hidden_dim]
model = DNN(hidden_dims = hidden_dims, num_classes = 10, dropout_ratio = 0.2, apply_batchnorm = True, apply_dropout = True, apply_activation = True, set_super = True)
optimizer = optim.Adam(model.parameters(), lr = lr)
3-3-3. 모델 학습
def training(model, dataloader, train_dataset, criterion, optimizer, device, epoch, num_epochs):
model.train() # 모델을 학습 모드로 설정
train_loss = 0.0
train_accuracy = 0
for images, labels in tqdm(dataloader):
images = images.to(device)
labels = labels.to(device)
# 순전파
outputs = model(images)
loss = criterion(outputs, labels)
# 역전파 및 weights 업데이트
optimizer.zero_grad()
loss.backward()
optimizer.step()
# 손실과 정확도 계산
train_loss += loss.item()
# torch.max에서 dim 인자에 값을 추가할 경우, 해당 dimension에서 최댓값과 최댓값에 해당하는 인덱스를 반환
_, predicted = torch.max(outputs, 1)
train_accuracy += (predicted == labels).sum().item()
# tqdm의 진행바에 표시될 설명 텍스트를 설정
tqdm(dataloader).set_description(f"Epoch [{epoch+1}/{num_epochs}], Train Loss: {loss.item():.4f}")
# 에폭별 학습 결과 출력
train_loss = train_loss / len(dataloader)
train_accuracy = train_accuracy / len(train_dataset)
return model, train_loss, train_accuracy
코드를 line by line으로 설명해 보자면,
우선 모델을 학습모드 `model.train()`으로 설정해 주고 loss와 accuracy를 초기화해 준다.
model.train을 해주면 drop out과 batch normalization처럼 학습과정에서만 진행되어야 하는 블록들을 활성화시켜준다.
다음으로 batch size에 따라서 이미지와 라벨을 gpu로도 학습할 수 있도록 구현하기 위해 device에 올려준다.
그리고 선언한 model에 이미지를 넣고, loss를 계산하는 순전파를 진행하고
back propagation을 진행하기 위해 `optimizer.zero_grad`를 이용해서 이전 iteration의 gradient를 초기화해줘야 한다.
초기화를 따로 해주지 않으면 이전 그래디언트의 정보가 누적되어 이상하게 업데이트가 진행된다.
다음으로 `loss.backward`를 이용하여 역전파를 진행하여 나온 결과를 토대로 `optimzer.step`을 이용해 파라미터들을 업데이트해준다. 이후 학습되는 과정을 확인하기 위해서 예측결과의 loss를 계산하여 확인할 수 있도록 해줬다.
3-3-4. 모델 평가
def evaluation(model, dataloader, valid_dataset, criterion, device, epoch, num_epochs):
model.eval() # 모델을 평가 모드로 설정
valid_loss = 0.0
valid_accuracy = 0
with torch.no_grad(): # model의 업데이트 막기
for images, labels in tqdm(dataloader):
images = images.to(device)
labels = labels.to(device)
# 순전파
outputs = model(images)
loss = criterion(outputs, labels)
# 손실과 정확도 계산
valid_loss += loss.item()
# torch.max에서 dim 인자에 값을 추가할 경우, 해당 dimension에서 최댓값과 최댓값에 해당하는 인덱스를 반환
_, predicted = torch.max(outputs, 1)
valid_accuracy += (predicted == labels).sum().item()
# tqdm의 진행바에 표시될 설명 텍스트를 설정
tqdm(dataloader).set_description(f"Epoch [{epoch+1}/{num_epochs}], Valid Loss: {loss.item():.4f}")
valid_loss = valid_loss / len(dataloader)
valid_accuracy = valid_accuracy / len(valid_dataset)
return model, valid_loss, valid_accuracy
다음으로 훈련된 모델을 평가하는 코드로 전체적인 구조는 비슷하기 때문에 차이점만 설명해 보겠다.
우선 `model.eval()`을 이용하여 평가모드로 모델을 설정해 주고, `with torch.no_grad()`를 이용하여 gradient가 업데이트되지 않도록 설정해 준다.
3-3-5. Training loop
위와 같이 한 epoch에 대한 train과 evaluation을 구현한 뒤 이것을 반복할 수 있도록 만들어줬다.
def training_loop(model, train_dataloader, valid_dataloader, criterion, optimizer, device, num_epochs, patience, model_name):
best_valid_loss = float('inf') # 가장 좋은 validation loss를 저장
early_stop_counter = 0 # 카운터
valid_max_accuracy = -1
for epoch in range(num_epochs):
model, train_loss, train_accuracy = training(model, train_dataloader, train_dataset, criterion, optimizer, device, epoch, num_epochs)
model, valid_loss, valid_accuracy = evaluation(model, valid_dataloader, valid_dataset, criterion, device, epoch, num_epochs)
if valid_accuracy > valid_max_accuracy:
valid_max_accuracy = valid_accuracy
# validation loss가 감소하면 모델 저장 및 카운터 리셋
if valid_loss < best_valid_loss:
best_valid_loss = valid_loss
torch.save(model.state_dict(), f"./model_{model_name}.pt")
early_stop_counter = 0
# validation loss가 증가하거나 같으면 카운터 증가
else:
early_stop_counter += 1
print(f"Epoch [{epoch + 1}/{num_epochs}], Train Loss: {train_loss:.4f}, Train Accuracy: {train_accuracy:.4f} Valid Loss: {valid_loss:.4f}, Valid Accuracy: {valid_accuracy:.4f}")
# 조기 종료 카운터가 설정한 patience를 초과하면 학습 종료
if early_stop_counter >= patience:
print("Early stopping")
break
return model, valid_max_accuracy
3-4. 결론
num_epochs = 100
patience = 3
scores = dict()
device = 'cuda:0' # gpu 설정
model_name = 'exp1'
init_method = 'kaiming' # gaussian, xavier, kaiming, zeros
model = DNN(hidden_dims = hidden_dims, num_classes = 10, dropout_ratio = 0.2, apply_batchnorm = True, apply_dropout = True, apply_activation = True, set_super = True)
model.weight_initialization(init_method)
model = model.to(device)
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr = lr)
model, valid_max_accuracy = training_loop(model, train_dataloader, valid_dataloader, criterion, optimizer, device, num_epochs, patience, model_name)
scores[model_name] = valid_max_accuracy
다음으로 지금까지 작성한 모델에 대한 학습을 진행하였다. 학습의 경우에는 다양하게 하이퍼 파라미터를 바꿔가며 실험해 보는 것이 좋다.
이렇게 불러온 모델을 이용해서 이제 test data에 대하여 추론을 진행한다.
model.eval()
total_labels = []
total_preds = []
total_probs = []
with torch.no_grad():
for images, labels in test_dataloader:
images = images.to(device)
labels = labels
outputs = model(images)
# torch.max에서 dim 인자에 값을 추가할 경우, 해당 dimension에서 최댓값과 최댓값에 해당하는 인덱스를 반환
_, predicted = torch.max(outputs.data, 1)
total_preds.extend(predicted.detach().cpu().tolist())
total_labels.extend(labels.tolist())
total_probs.append(outputs.detach().cpu().numpy())
total_preds = np.array(total_preds)
total_labels = np.array(total_labels)
total_probs = np.concatenate(total_probs, axis= 0)
분류모델이기 때문에 precision, recall, fl- score 등 다양한 평가지표를 이용해서 결과를 확인해 볼 수 있다.
precision = precision_score(total_labels, total_preds, average='macro')
recall = recall_score(total_labels, total_preds, average='macro')
f1 = f1_score(total_labels, total_preds, average='macro')
total_probs = np.exp(total_probs)
auc = roc_auc_score(total_labels, total_probs, average='macro', multi_class = 'ovr')
print(f'Precision: {precision}, Recall: {recall}, F1 Score: {f1}, AUC: {auc}')
이렇게 추론한 것에 대한 결과를 확인해보면 다음과 같이 예측된 라벨들이 `total_labels`에 저장되어있고 해당 train image와 비교해볼 수 있다.
idx = 7777 # 0 to 9999
images= test_dataset[idx]
predicted_label = total_labels[idx]
# 이미지 시각화
imshow(images[0][0], cmap='gray')
# 예측된 레이블을 제목으로 추가
plt.title("Predicted Label: {predicted_label}")
plt.show()
4. Model -CNN
4-1. 모델 구조
# MNIST 분류를 위한 CNN 모델
class CNN(nn.Module):
def __init__(self, num_classes, dropout_ratio):
super(CNN, self).__init__()
self.num_classes = num_classes
self.layer = nn.Sequential(
nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5), # [BATCH_SIZE, 1, 28, 28] -> [BATCH_SIZE, 16, 24, 24]
nn.ReLU(), # ReLU 활성화 함수 적용
nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5), # [BATCH_SIZE, 16, 24, 24] -> [BATCH_SIZE, 32, 20, 20]
nn.ReLU(), # ReLU 활성화 함수 적용
nn.MaxPool2d(kernel_size=2), # [BATCH_SIZE, 32, 20, 20] -> [BATCH_SIZE, 32, 10, 10]
nn.Dropout(dropout_ratio),
nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5), # [BATCH_SIZE, 32, 10, 10] -> [BATCH_SIZE, 64, 6, 6]
nn.ReLU(),
nn.MaxPool2d(kernel_size=2), # 크기를 1/2로 줄입니다. [BATCH_SIZE, 64, 6, 6] -> [BATCH_SIZE, 64, 3, 3]
nn.Dropout(dropout_ratio),
)
self.fc_layer = nn.Linear(64*3*3, self.num_classes) # [BATCH_SIZE, 64*3*3] -> [BATCH_SIZE, num_classes]
self.softmax = nn.LogSoftmax(dim = 1)
def forward(self,x):
'''
Input: [batch_size, channel, height, width]
Output: [batch_size, num_classes]
'''
out = self.layer(x) # [BATCH_SIZE, 64, 3, 3]
out = out.view(x.size(0), -1) # [BATCH_SIZE, 64*3*3]
pred = self.fc_layer(out) # [BATCH_SIZE, num_classes]
pred = self.softmax(pred) # [BATCH_SIZE, num_classes]
return pred
def weight_initialization(self):
for m in self.modules():
if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):
nn.init.kaiming_normal_(m.weight)
nn.init.zeros_(m.bias)
def count_parameters(self):
return sum(p.numel() for p in self.parameters() if p.requires_grad)
전체적인 구조 자체는 DNN과 동일한데 우선 모델을 정의하고, 순전파 과정을 진행하고, 가중치 초기화하는 내용들이 담겨이다. 모델을 선언하는 부분에서만 차이를 보이기 때문에 해당 부분만 설명해보도록 하겠다.
우선 모델을 이전에는 블럭 하나하나를 선언하고 forward부분에서 반복문을 이용하여 원하는 블럭을 배치하여 모델을 만들었었는데, 이번에는 sequential을 이용하여서 안에 있는 layer들을 순차적으로 선언하는 방법을 사용하였다. 이렇게 선언한 sequential layer들은 forward에서 `self.layer()`을 통해서 한번에 통과시킬 수 있었다.
또한 기존의 DNN에서 사용했던 fc, fully connect layer이 아니라, `torch.nn.conv2d`를 쌓아서 모델을 선언해줬다.`torch.nn.Conv2d`의 파라미터는 다음과 같다.
`in_channels` : input 값의 채널
`out_channels` : convolution 연산을 통해 생성된 아웃풋의 채널
`kernel_size` : 필터의 크기
`stride` : 필터의 이동 간격
`padding` : 입력 데이터 주변에 추가되는 값(padding)의 크기
또한 활성화함수를 거친다음 `pooling layer`을 max pooling을 이용하여 쌓아줬다.
최종적으로 flatten 시킨다음 softmax함수를 통해서 분류를 시도하였다.
forward에서의 차이점은 이전에 DNN은 모두 flatten시켜서 사용했었는데, CNN은 그러지 않아도 되기때문에 해당 과정을 스킵하였고 이전에 쌓았던 layer들을 이용하여 순전파를 진행해줬다.
4-2. 모델 학습 및 추론
위의 모델을 선언하는 과정만 다르고 모든 과정은 DNN을 이용했을 때와 동일하다.
가중치 초기화 함수, 최적화함수, 손실함수 등을 여러 실험을 통해 결정해준뒤 동일한 코드를 이용하여 모델의 결과를 확인해볼 수 있었다.
'ML & DL > 개념정리' 카테고리의 다른 글
Pretrained Model (0) | 2024.01.09 |
---|---|
PyTorch 프로세스 (1) | 2024.01.09 |
딥러닝 발전 (0) | 2023.12.28 |
Linear, Lasso, Ridge Regression (0) | 2023.12.27 |
Random Forest: 랜덤포레스트 (0) | 2023.12.26 |