내 풀이 github LINK: https://github.com/qkrtmdtj04/CS231n-Assignment
GitHub - qkrtmdtj04/CS231n-Assignment
Contribute to qkrtmdtj04/CS231n-Assignment development by creating an account on GitHub.
github.com
분명 저번 글에서 Assignment 3: Q1을 했는데 또하고 있냐면, 추측하기로는 파이토치에 버전 차이 때문에 2024버전에서 Transformer 문제를 풀때 계속 오류가 난다. 그래서 새로운 개념이 추가된 CS231n 2025버전 영상도 다시 보고 있고 이참에 2026버전으로 Transformer와 ViT내용이 있는 과제를 하는 편이 좋을거 같아서, 이제부터 CS231n 2026 Assignment3를 토대로 문제를 풀것이다. //LSTM이 2026버전 과제에 없어서 그부분은 나중에 풀어보도록 하겠다.
Transformers의 필요성
Transformers의 필요성 지난 RNN 파트에서 다뤘듯이, RNN은 이전 hidden state를 순차적으로 넘겨받으며 시퀀스를 처리한다. 이 구조 덕분에 순서 정보를 자연스럽게 반영할 수 있지만, 동시에 두 가지 치명적인 한계도 함께 따라온다.
- 기울기 소실 문제다. sequence가 길어질수록 초반 timestep의 정보가 후반까지 온전히 전달되기 어렵다.
- 병렬화가 안 된다는 점이다. t번째 hidden state를 구하려면 반드시 t-1번째 hidden state가 먼저 계산되어 있어야 하므로, GPU를 아무리 많이 써도 시간축 방향으로는 순차 연산을 피할 수 없다.
Transformer는 이 두 문제를 아예 recurrence 자체를 없애는 방식으로 해결한다. 대신 그 자리를 Self-Attention이 채운다.
Attention의 핵심 아이디어
- Transformer에서 각 위치(token)는 자기 자신을 포함한 시퀀스의 모든 위치를 직접 참조할 수 있다. 이 "직접 참조"는 Query(Q), Key(K), Value(V) 세 가지 벡터 간의 유사도 계산을 통해 이루어진다.
- 순차적인 연산(recurrence)이 없으므로 모든 timestep을 동시에 계산하여 완벽한 병렬화가 가능한다. 이를 수식으로 표현하면 다음과 같다.

- Query(Q)와 Key(K)의 내적: 두 단어가 "얼마나 관련이 있는가(유사도)"를 구한다.
- Scaling ($\sqrt{d_k}$): 내적 값이 너무 커져 softmax의 기울기가 0에 수렴하는 현상을 방지하기 위해 스케일링을 거친다.
- Softmax: 유사도 값을 0~1 사이의 확률값(가중치)으로 정규화한다.
- Value(V) 곱하기: 구해진 가중치를 각 단어가 가진 실제 의미(Value)에 곱해 가중합(Weighted Sum)을 구한다.
즉, 현재 위치의 단어가 문장 내의 다른 모든 단어들을 얼마나 참고해야 하는지를 스스로 학습하는 구조라고 이해하면 된다.
Transformers architecture 설명


논문 "Attention Is All You Need"에서 제안된 Transformer 모델은 크게 입력값을 압축하는 Encoder와 이를 바탕으로 결과를 생성하는 Decoder로 나뉜다. (과제에서 다룰 ViT 등 많은 최신 모델들은 이 중 Encoder 구조만 떼어내어 사용하기도 한다.)
Transformer 아키텍처를 구성하는 핵심 블록들은 다음과 같다.
1. Positional Encoding
Transformer는 RNN처럼 순차적으로 단어를 입력받지 않고 한 번에 모든 단어를 병렬로 입력받습니다. 이로 인해 단어의 '위치(순서)' 정보가 사라지는 문제가 발생한다. 이를 해결하기 위해 입력 임베딩 벡터에 사인(Sine)과 코사인(Cosine) 함수를 이용한 위치 정보(Positional Encoding) 값을 더해준다. 이를 통해 모델은 "어떤 단어가 문장의 어느 위치에 있는지"를 파악할 수 있게 된다.
2. Multi-Head Attention
하나의 Self-Attention만 수행하는 것이 아니라, Q, K, V를 여러 개의 'Head'로 나누어 동시에 병렬로 Attention을 수행한다.
- 여러개로 나누는 이유는 문장을 해석할 때 하나의 관점만 고집하는 것이 아니라, 문법적 관계, 의미적 관계, 위치적 관계 등 다양한 관점에서 문맥을 파악하기 위해서다. 여러 Head에서 나온 결과를 마지막에 하나로 합쳐서(Concat) 더 풍부한 특징을 잡아낸다.
3. Position-wise Feed-Forward Network (FFN)
Attention 층을 통과한 출력 벡터는 각각 독립적으로 2개의 선형 변환(Linear Transformation)과 ReLU(혹은 GELU) 활성화 함수를 거친다. Attention이 '단어와 단어 사이의 관계'를 파악하는 역할이라면, FFN은 그 파악된 정보를 바탕으로 복잡한 패턴을 학습하고 비선형성을 부여하는 역할을 한다.
4. Residual Connection & Layer Normalization (Add & Norm)
신경망이 깊어질수록 학습이 어려워지는 문제를 해결하기 위해 도입된 장치이다.
- Add (Residual Connection): 입력값을 층(Layer)의 출력값에 그대로 더해주는(Skip Connection) 방식이다. 정보의 손실을 막고 기울기 소실 문제를 완화한다.
- Norm (Layer Normalization): 각 층의 활성화 값들을 정규화하여 학습 과정을 안정시키고 속도를 높인다.
이러한 Encoder 블록(Multi-Head Attention + FFN + Add & Norm)을 N번 반복해서 쌓아 올린 것이 바로 Transformer의 뼈대이다.
ViT 간단 설명(과제를 위한, 자세한 내용은 ViT 논문 리뷰로)

.
Vision Transformer(ViT)는 자연어 처리에서 강력한 성능을 보인 Transformer 구조를 비전(Vision) 태스크에 적용하기 위해 고안된 아키텍처이다. 과제 수행을 위해 위 이미지의 핵심 동작 흐름을 단계별로 정리해 보겠다.
- 패치 분할 (Break into patches): 먼저 입력 이미지(예: 224x224x3)를 고정된 크기의 작은 패치(예: 16x16x3)들로 쪼갠다.
- 평탄화 및 선형 변환 (Flatten and apply a linear transform): 쪼개진 패치들을 평탄화(Flatten)한 후, 선형 변환을 거쳐 D차원 벡터로 만든다.
- 입력 벡터 생성: 이 D차원 벡터들이 바로 Transformer 구조에 들어가는 각각의 입력 벡터가 된다.
- 위치 인코딩 (Positional encoding): Transformer 모델에게 각 패치의 2D 위치 정보를 알려주기 위해 위치 인코딩을 사용한다.
- Transformer 연산:
- ViT의 Transformer 내부는 Layer Normalization, Self-Attention, 그리고 MLP(Multi-Layer Perceptron) 블록들로 구성되어 있다.
- 여기서는 어떠한 마스킹(Masking)도 사용하지 않으며, 각각의 이미지 패치는 다른 모든 이미지 패치들을 자유롭게 참조할 수 있다.
- 이 연산을 거치면 Transformer는 각 패치당 하나의 출력 벡터를 제공한다.
- 클래스 예측 (Predict class scores): 최종적으로 출력된 NxD 형태의 벡터들을 1xD 차원으로 평균 풀링(Average pool)한다. 이후 선형 레이어(Linear layer)를 통과시켜 최종적인 클래스 점수를 예측하게 됩니다.
Q1 Transformer 풀이
Q1-1. MultiHeadAttention
class MultiHeadAttention(nn.Module):
def __init__(self, embed_dim, num_heads, dropout=0.1):
super().__init__()
assert embed_dim % num_heads == 0
self.key = nn.Linear(embed_dim, embed_dim)
self.query = nn.Linear(embed_dim, embed_dim)
self.value = nn.Linear(embed_dim, embed_dim)
self.proj = nn.Linear(embed_dim, embed_dim)
self.attn_drop = nn.Dropout(dropout)
self.n_head = num_heads
self.emd_dim = embed_dim
self.head_dim = self.emd_dim // self.n_head
def forward(self, query, key, value, attn_mask=None):
N, S, E = query.shape
N, T, E = value.shape
# Create a placeholder, to be overwritten by your code below.
output = torch.empty((N, S, E))
############################################################################
############################################################################
H = self.n_head
Wq = self.query(query).reshape((N,S,H,E//H)).transpose(1,2)
Wk = self.key(key).reshape((N,T,H,E//H)).transpose(1,2)
Wv = self.value(value).reshape((N,T,H,E//H)).transpose(1,2)
#MatMul,scale
matmul_WqWk = torch.matmul(Wq,Wk.transpose(2,3))
scale_WqWk = matmul_WqWk / math.sqrt(self.emd_dim/H)
#Mask
if attn_mask != None:
scale_WqWk = scale_WqWk.masked_fill(attn_mask==0, float("-inf"))
#softMax
softMax = self.attn_drop(torch.nn.functional.softmax(scale_WqWk, dim=-1))
#MatMul
matmul_WqWkWv = torch.matmul(softMax,Wv)
#Concat
concat_WqWkWv = torch.reshape(torch.transpose(matmul_WqWkWv,1,2),(N,S,E))
output = self.proj(concat_WqWkWv) #d*d
############################################################################
############################################################################
return output
이 블록은 Transformer의 핵심인 Multi-Head Self-Attention을 구현한 부분이다.
- Query, Key, Value 변환 및 분할: 입력된 query, key, value에 각각 선형 변환(nn.Linear)을 적용한 후, .reshape()과 .transpose()를 사용해 텐서를 지정된 num_heads(H) 개수로 나눈다.
- Attention Score 및 Scaling: Q와 K의 전치 행렬을 내적(MatMul)하여 유사도를 구하고, 모델 차원수에 맞춰 스케일링(scale_WqWk)을 진행한다.
- 마스킹(Masking): 미래의 정보를 참조하면 안 되는 경우를 위해, attn_mask가 주어지면 해당 위치의 값을 -inf로 덮어씌워(masked_fill) softmax 이후 가중치가 0이 되게 만든다.
- Softmax 및 가중합: Softmax와 Dropout을 거쳐 정규화된 가중치를 만든 뒤, V와 행렬 곱(MatMul)을 수행해 최종 문맥 벡터를 얻어냅니다. 이후 분할되었던 Head들을 다시 병합(Concat)하고 최종 self.proj를 통과시킨다.
Q1-2. Positional Encoding
class PositionalEncoding(nn.Module):
def __init__(self, embed_dim, dropout=0.1, max_len=5000):
super().__init__()
self.dropout = nn.Dropout(p=dropout)
assert embed_dim % 2 == 0
pe = torch.zeros(1, max_len, embed_dim)
############################################################################
############################################################################
for pos in range(max_len):
for i in range(embed_dim):
if i % 2 == 0:
pe[0,pos,i] = math.sin(pos/(10000**(i/embed_dim)))
else:
pe[0,pos,i] = math.cos(pos/(10000**((i-1)/embed_dim)))
############################################################################
# END OF YOUR CODE #
############################################################################
# Make sure the positional encodings will be saved with the model
# parameters (mostly for completeness).
self.register_buffer('pe', pe)
def forward(self, x):
N, S, D = x.shape
# Create a placeholder, to be overwritten by your code below.
output = torch.empty((N, S, D))
############################################################################
############################################################################
output = x + self.pe[:, :S]
output = self.dropout(output)
############################################################################
# END OF YOUR CODE #
############################################################################
return output

- 병렬 연산으로 잃어버린 '순서' 정보를 부여하는 단계입니다. max_len 길이만큼 미리 위치 정보를 계산하여 pe 버퍼에 저장해 둔다.
- 삼각함수를 활용하는데, 코드의 반복문을 보면 짝수 인덱스(i % 2 == 0)에는 사인(Sine) 함수를, 홀수 인덱스에는 코사인(Cosine) 함수를 적용하는 것을 확인할 수 있다. forward 함수에서는 입력 텐서 x에 시퀀스 길이 S만큼 잘라낸 위치 정보(self.pe[:, :S])를 단순히 요소별로 더해주어(Add) 위치 정보를 임베딩한다.
Q1-3. TransformerDecoderLayer
class TransformerDecoderLayer(nn.Module):
def __init__(self, input_dim, num_heads, dim_feedforward=2048, dropout=0.1):
super().__init__()
self.self_attn = MultiHeadAttention(input_dim, num_heads, dropout)
self.cross_attn = MultiHeadAttention(input_dim, num_heads, dropout)
self.ffn = FeedForwardNetwork(input_dim, dim_feedforward, dropout)
self.norm_self = nn.LayerNorm(input_dim)
self.norm_cross = nn.LayerNorm(input_dim)
self.norm_ffn = nn.LayerNorm(input_dim)
self.dropout_self = nn.Dropout(dropout)
self.dropout_cross = nn.Dropout(dropout)
self.dropout_ffn = nn.Dropout(dropout)
def forward(self, tgt, memory, tgt_mask=None):
# Self-attention block (reference implementation)
shortcut = tgt
tgt = self.self_attn(query=tgt, key=tgt, value=tgt, attn_mask=tgt_mask)
tgt = self.dropout_self(tgt)
tgt = tgt + shortcut
tgt = self.norm_self(tgt)
############################################################################
############################################################################
# cross-attention blook
shortcut = tgt
tgt = self.cross_attn (query=tgt, key=memory, value=memory)
tgt = self.dropout_self(tgt)
tgt = tgt + shortcut
tgt = self.norm_self(tgt)
shortcut = tgt
tgt = self.ffn(tgt)
tgt = self.dropout_self(tgt)
tgt = tgt + shortcut
tgt = self.norm_self(tgt)
############################################################################
# END OF YOUR CODE #
############################################################################
return tgt
Transformer의 Decoder 한 층을 구성하는 코드이다. 총 3개의 서브 블록(Self-attention, Cross-attention, FFN)으로 이루어진다.
- Masked Self-Attention: 타겟(tgt) 시퀀스 스스로를 Q, K, V로 사용하여 어텐션을 수행합니다. 이때 미래의 단어를 보지 못하도록 tgt_mask가 적용 된다.
- Cross-Attention: Decoder의 출력을 Query로, Encoder에서 추출된 입력 이미지(또는 소스 문장)의 정보인 memory를 Key와 Value로 사용하여 연산한다. 즉, 생성할 텍스트가 원본 데이터의 어떤 부분을 집중해서 봐야 하는지 학습하는 단계이다.
- FFN: 앞서 파악된 관계 정보를 바탕으로 비선형 변환을 수행합니다. 각 단계마다 어김없이 잔차 연결(Residual Connection: tgt = tgt + shortcut)과 정규화(LayerNorm)가 적용되어 학습 안정성을 높이고 있다.
Q1-4. Transformer for Image Captioning
class CaptioningTransformer(nn.Module):
def __init__(self, word_to_idx, input_dim, wordvec_dim, num_heads=4,
num_layers=2, max_length=50):
super().__init__()
vocab_size = len(word_to_idx)
self.vocab_size = vocab_size
self._null = word_to_idx["<NULL>"]
self._start = word_to_idx.get("<START>", None)
self._end = word_to_idx.get("<END>", None)
self.visual_projection = nn.Linear(input_dim, wordvec_dim)
self.embedding = nn.Embedding(vocab_size, wordvec_dim, padding_idx=self._null)
self.positional_encoding = PositionalEncoding(wordvec_dim, max_len=max_length)
decoder_layer = TransformerDecoderLayer(input_dim=wordvec_dim, num_heads=num_heads)
self.transformer = TransformerDecoder(decoder_layer, num_layers=num_layers)
self.apply(self._init_weights)
self.output = nn.Linear(wordvec_dim, vocab_size)
def _init_weights(self, module):
"""
Initialize the weights of the network.
"""
if isinstance(module, (nn.Linear, nn.Embedding)):
module.weight.data.normal_(mean=0.0, std=0.02)
if isinstance(module, nn.Linear) and module.bias is not None:
module.bias.data.zero_()
elif isinstance(module, nn.LayerNorm):
module.bias.data.zero_()
module.weight.data.fill_(1.0)
def forward(self, features, captions):
N, T = captions.shape
# Create a placeholder, to be overwritten by your code below.
scores = torch.empty((N, T, self.vocab_size))
############################################################################
############################################################################
#print(features.shape)
features = self.visual_projection(features)
features = features.unsqueeze(dim=1)
tgt = self.embedding(captions)
tgt = self.positional_encoding(tgt)
tgt_mask = torch.tril(torch.ones(T, T))
tgt = self.transformer(tgt,features,tgt_mask)
scores= self.output(tgt)
############################################################################
# END OF YOUR CODE #
############################################################################
return scores
지금까지 만든 모듈들을 조립해 이미지를 설명하는 텍스트를 생성(Image Captioning)하는 모델을 완성한다.
- 이미지 특징 처리: 입력된 이미지 특성(features)은 visual_projection을 거쳐 차원이 맞춰지고(wordvec_dim), Decoder의 memory로 들어가게 된다.
- 텍스트 캡션 처리: 정답 캡션(captions)은 단어 임베딩(self.embedding)과 위치 인코딩(self.positional_encoding)을 거쳐 타겟 정보(tgt)가 된다 .
- Masking 및 Decoder: torch.tril을 사용해 하삼각행렬 형태의 마스크(tgt_mask)를 생성하여 모델이 미래 시점의 단어를 컨닝하지 못하게 막는다. 이후 전체가 self.transformer를 통과하고, 마지막 선형 계층(self.output)을 통해 다음 단어 예측을 위한 확률 점수(scores)를 반환한다.
Q1-5. ViT - PatchEmbedding
class PatchEmbedding(nn.Module):
def __init__(self, img_size, patch_size, in_channels=3, embed_dim=128):
super().__init__()
self.img_size = img_size
self.patch_size = patch_size
self.in_channels = in_channels
self.embed_dim = embed_dim
assert img_size % patch_size == 0, "Image dimensions must be divisible by the patch size."
self.num_patches = (img_size // patch_size) ** 2
self.patch_dim = patch_size * patch_size * in_channels
# Linear projection of flattened patches to the embedding dimension
self.proj = nn.Linear(self.patch_dim, embed_dim)
def forward(self, x):
N, C, H, W = x.shape
assert H == self.img_size and W == self.img_size, \
f"Expected image size ({self.img_size}, {self.img_size}), but got ({H}, {W})"
out = torch.zeros(N, self.embed_dim)
############################################################################
############################################################################
#print(x.shape)
x = x.reshape(N, C, H // self.patch_size, self.patch_size, W // self.patch_size, self.patch_size)
#print(x.shape)
x = x.permute(0,2,4,1,3,5).reshape((N,self.num_patches,-1))
#print(x.shape)
out = self.proj(x)
#print(self.patch_dim)
#print(out.shape)
############################################################################
# END OF YOUR CODE #
############################################################################
return out
- Vision Transformer(ViT)의 첫 단추인 이미지를 패치 단위로 분할하고 평탄화하는 모듈이다.
- PyTorch의 차원 조작 메서드를 적극 활용한다. .reshape()과 .permute()를 사용해 (N, C, H, W) 형태의 원본 이미지를 패치 크기에 맞게 (N, 전체 패치 개수, C * patch_size * patch_size) 형태로 변환한다.
- 이후 평탄화된 각 패치를 self.proj 선형 계층에 통과시켜 Transformer가 처리할 수 있는 고정된 차원(embed_dim)의 토큰으로 임베딩한다.
Q1-6. ViT - TransformerEncoderLayer
class TransformerEncoderLayer(nn.Module):
def __init__(self, input_dim, num_heads, dim_feedforward=2048, dropout=0.1):
super().__init__()
self.self_attn = MultiHeadAttention(input_dim, num_heads, dropout)
self.ffn = FeedForwardNetwork(input_dim, dim_feedforward, dropout)
self.norm_self = nn.LayerNorm(input_dim)
self.norm_ffn = nn.LayerNorm(input_dim)
self.dropout_self = nn.Dropout(dropout)
self.dropout_ffn = nn.Dropout(dropout)
def forward(self, src, src_mask=None):
############################################################################
############################################################################
self_src = self.self_attn(src,src,src,src_mask)
self_src = self.dropout_self(self_src)
norm_src = self.norm_self(self_src+src)
ffn_src = self.ffn(norm_src)
ffn_src = self.dropout_ffn(ffn_src)
src = self.norm_ffn(ffn_src +norm_src)
############################################################################
# END OF YOUR CODE #
############################################################################
return src
- ViT에서 사용하는 Encoder 블록입니다. 구조는 매우 직관적이다. Multi-Head Self-Attention을 통해 패치들 간의 전역적인 연관성을 파악하고, FeedForward Network(FFN)를 통해 각 패치 벡터 내부에 복잡한 패턴과 비선형성을 부여한다. 각 단계마다 self_src + src 형태의 잔차 연결(Residual Connection)과 Layer Normalization이 순차적으로 적용되어 깊은 네트워크에서도 기울기가 잘 흐르도록 돕는다.
Q1-7. ViT - Model
class VisionTransformer(nn.Module):
def __init__(self, img_size=32, patch_size=8, in_channels=3,
embed_dim=128, num_layers=6, num_heads=4,
dim_feedforward=256, num_classes=10, dropout=0.1):
super().__init__()
self.num_classes = num_classes
self.patch_embed = PatchEmbedding(img_size, patch_size, in_channels, embed_dim)
self.positional_encoding = PositionalEncoding(embed_dim, dropout=dropout)
encoder_layer = TransformerEncoderLayer(embed_dim, num_heads, dim_feedforward, dropout)
self.transformer = TransformerEncoder(encoder_layer, num_layers=num_layers)
# Final classification layer to predict class scores from pooled token.
self.head = nn.Linear(embed_dim, num_classes)
self.apply(self._init_weights)
def _init_weights(self, module):
if isinstance(module, (nn.Linear, nn.Embedding)):
module.weight.data.normal_(mean=0.0, std=0.02)
if isinstance(module, nn.Linear) and module.bias is not None:
module.bias.data.zero_()
elif isinstance(module, nn.LayerNorm):
module.bias.data.zero_()
module.weight.data.fill_(1.0)
def forward(self, x):
N = x.size(0)
logits = torch.zeros(N, self.num_classes, device=x.device)
############################################################################
############################################################################
patch_x = self.patch_embed(x)
#print(patch_x.shape)
point_x = self.positional_encoding(patch_x)
trans_x = self.transformer(point_x)
trans_x = trans_x.mean(axis=1)
#print(trans_x.shape)
logits += self.head(trans_x)
############################################################################
# END OF YOUR CODE #
############################################################################
return logits
드디어 전체 Vision Transformer 아키텍처를 하나로 조립하는 단계다.
- PatchEmbedding을 통해 2D 이미지를 1D 패치 토큰 시퀀스로 만든다.
- PositionalEncoding을 적용해 각 패치가 이미지의 어느 위치에 있었는지 정보를 더해준다.
- 이를 TransformerEncoder 여러 층에 통과시켜 이미지 전체 문맥이 융합된 특징 벡터들을 추출한다.
- 모든 패치 토큰들의 특징을 하나로 종합하기 위해 시퀀스 차원(axis=1)을 기준으로 평균을 구한다(trans_x.mean(axis=1)). 논문에서는 별도의 [CLS] 토큰을 사용하기도 하지만, 과제에서는 평균 풀링(Average Pooling) 방식을 채택했다.
- 마지막으로 head (Linear 레이어)를 통과시켜 최종 클래스의 분류 점수(logits)를 산출한다.
Q1-8. ViT - CIFAR-10 데이터셋을 학습한 모델 정확도 45% 넘기기 미션
from cs231n.classification_solver_vit import ClassificationSolverViT
############################################################################
# TODO: Train a Vision Transformer model that achieves over 0.45 test #
# accuracy on CIFAR-10 after 2 epochs by adjusting the model architecture #
# and/or training parameters as needed. #
# #
# Note: If you want to use a GPU runtime, go to `Runtime > Change runtime #
# type` and set `Hardware accelerator` to `GPU`. This will reset Colab, #
# so make sure to rerun the entire notebook from the beginning afterward. #
############################################################################
learning_rate = 1e-3
weight_decay = 1e-4
batch_size = 128
model = VisionTransformer(
patch_size=4,
num_layers=6,
num_heads=8,
)
################################################################################
# END OF YOUR CODE #
################################################################################
solver = ClassificationSolverViT(
train_data=train_data,
test_data=test_data,
model=model,
num_epochs = 2, # Don't change this
learning_rate = learning_rate,
weight_decay = weight_decay,
batch_size = batch_size,
)
solver.train('cuda' if torch.cuda.is_available() else 'cpu')
- 단 2 Epoch 만에 테스트 정확도 45%를 돌파해야 하는 미션입니다. 이 2 Epoch 상당히 머리를 아프게 했다.
- 나의 직관은 이미지가 쪼개지면 더 관계를 알 수 있지 않을까 생각했다. 그래서 patch_size를 4로 설정하여 이미지를 더 잘게 쪼개어 패치의 개수를 늘렸다.
- 또한 모델의 표현력을 높이기 위해 num_layers를 6으로, num_heads를 8로 설정해 깊이와 관점의 다양성을 확장했습니다. 결과적으로 테스트 세트에서 48.32%의 정확도를 기록하며 미션을 여유롭게 통과했다.
Accuracy on test set: 0.4832
'DeepLearning > CS231n' 카테고리의 다른 글
| CS231n 2024 Assignment 3: Q1: RNN (0) | 2026.07.17 |
|---|---|
| CS231n 2024 Assignment 2: Q5: PyTorch (0) | 2026.06.21 |
| CS231n Assignment 2: Q4: ConvolutionalNetworks (0) | 2026.06.07 |
| CS231n Assignment 2: Q3: Dropout (0) | 2026.06.06 |
| CS231n Assignment 2: Q2: Batch Normalization (0) | 2026.05.30 |