신경망에서 일어나는 모든 연산과 최적화는 텐서연산이다. 가중치와 바이어스 같은 모든 파라미터도 텐서다.
그런데 어떻게 비디오나 텍스트 데이터를 텐서로 변환했을때 훈련시키기에 적합하도록 가공할 수 있는걸까?에 대해서 이해해보자.
이미지다루기
이미지는 픽셀 단위의 높이와 너비을 가지는 표준적인 그리드에 나열된 복수개의 스칼라 값 모음으로 표현된다
컬러채널 더하기
컬러를 숫자로 인코딩 하는 방법은 다양하다.
가장 흔한 방법으로 rgb. 세 개 숫자로 컬러를 정의하는 방식이다.
이미지파일로딩 imageio
여러 포맷으로 저장된 이미지는 파이썬에서는 다양한 방식으로 이미지 로딩이 가능하다.
pip install imageio
import imageio.v2 as imageio
img = imageio.imread("sample.jpg")
print(type(img))
print(img.shape)
레이아웃 변경하기
구식 차원 레이아웃에서 적절한 레이아웃을 얻기 위해 새로운 차원으로 변경하려면 텐서 permute 메소드를 사용한다.
3장에서 잠시 경험해봤다.
import torch
import imageio.v2 as imageio
img = imageio.imread("cat.jpg")
tensor = torch.from_numpy(img)
print(tensor.shape)
torch.Size([480, 640, 3])
tensor = tensor.permute(2, 0, 1)
print(tensor.shape)
torch.Size([3, 480, 640])
데이터정규화
신경망은 일반적으로 부동소수점 텐서를 입력으로 사용한다.
대략 0에서 1사이거나 -1 에서 1사이일때 훈련성능이 가장 좋은 특징을 띈다.
import torch
x = torch.tensor([170., 180., 190.])
x_norm = (x - x.min()) / (x.max() - x.min())
print(x_norm)
img = torch.tensor([0., 128., 255.])
img = (img / 255.0 - 0.5) / 0.5
print(img)
테이블 데이터 표현하기
딥러닝에서는 csv 파일을 읽은 후 데이터를 pytorch tensor로 변환하여 사용한다. 대표적인 방법으로는 파이썬 내장 csv, numpy, pandas를 사용하는 방법이 있다.
import csv
data = []
with open('data.csv', 'r') as f:
reader = csv.reader(f)
next(reader) # 헤더 제거
for row in reader:
data.append([float(row[0]), float(row[1])])
print(data)
import numpy as np
data = np.loadtxt(
'data.csv',
delimiter=',',
skiprows=1
)
print(data)
import pandas as pd
df = pd.read_csv('data.csv')
print(df)
점수표현하기
와인데이터가있을때 산도, 당도, 알코올은 입력 특성 feature에 해당한다. 품질점수는 정답 label이다. 회귀로 접근해 품질점수를 숫자 그대로 예측하거나 분류로 접근해 품질점수를 등급으로 봐도된다. 입력에 품질점수를 포함하면 정답을 미리 알기때문에 분리한다.
import pandas as pd
import torch
df = pd.read_csv("wine.csv")
# 입력
X = df.drop("quality", axis=1)
# 정답
y = df["quality"]
X_tensor = torch.tensor(
X.values,
dtype=torch.float32
)
y_tensor = torch.tensor(
y.values
)
print(X_tensor.shape)
print(y_tensor.shape)
원핫 인코딩
점수를 처리하는 또다른 방법 1부터 10까지의 값이 10개의 워소에 대응하도록 정해둔다.
원소하나를 1로 설정한다.
나머지는 모두 0으로 처리한다.
import pandas as pd
# 예제 데이터
df = pd.DataFrame({
'color': ['red', 'blue', 'green', 'red']
})
# 원-핫 인코딩
encoded_df = pd.get_dummies(df, columns=['color'])
print(encoded_df)
사과, 바나나, 포도등은 문자열을 바로 학습하기가 어려워 숫자로 변환하고 이때 0인 사과보다 2인 포도가 크다고 의미없는 관계를 학습할 수 있음으로 각 클래스 위치로만 표시한다.
사과 -> [1, 0, 0]
바나나 -> [0, 1, 0]
포도 -> [0, 0, 1]
unsqueeze()는 차원을 추가하는 함수로 (batch size, feature) 형태를 기대하는 딥러닝 모델에게 맞춰주는 것이다.
unsqueeze는 싱글톤 차원을 추가한다. 1차원 텐서가 값의 변화나 추가요소없이 2차원 텐서로 바뀌며 요소로 접근하기 위한 인덱스가 추가로 만들어졌다고 생각하면된다.
import torch
# 학생 한 명의 시험 점수 (1차원 텐서)
score = torch.tensor([80, 90, 100])
print("원본 텐서")
print(score)
print("shape:", score.shape) # torch.Size([3])
# 배치 차원 추가
score_batch = score.unsqueeze(0)
print("\nunsqueeze(0) 적용 후")
print(score_batch)
print("shape:", score_batch.shape) # torch.Size([1, 3])
# 값은 그대로이고, 배치 차원만 추가됨
print("\n원본 값:", score[1].item())
print("unsqueeze 후 값:", score_batch[0][1].item())
고급인덱싱, 파이토치 기능 data텐서를인덱싱할때 torch.bool 데이터 타입을 사용하면 열이 True에 해당하는 행들만 접근할 수 있다.
import torch
# 4명의 학생 점수 (행: 학생, 열: 과목)
data = torch.tensor([
[90, 85],
[70, 60],
[95, 100],
[65, 75]
])
print("원본 데이터")
print(data)
# 첫 번째 과목 점수가 80점 이상인 학생만 선택
mask = data[:, 0] >= 80
print("\nBoolean 마스크")
print(mask)
# Boolean 인덱싱
selected = data[mask]
print("\n선택된 학생 데이터")
print(selected)
테이블의 모든 행은 다른 행과 독립적으로 행의 순서는 중요하지 않았다. 소스 데이터에서 각 행은 시간별로 분리된 데이터다. 인덱스를 올릴때마다 일자가 바뀌고 다른 축에서는 하루의 시간을 표현하게 만든다면 각 행은 연속적인 시간상에서 한 지점을 표현한다. 레코드 인덱스 instant, 일자, 계절, 연도, 월, 시간, 공휴일상태, 요일, 평일상태...
2년치 데이터셋을 쪼개서 다소 넓은 관찰 주기로 나눌 필요가 있을지도 모른다.
import pandas as pd
# 시간 순서대로 정렬된 시계열 데이터
df = pd.DataFrame({
"date": pd.date_range("2023-01-01", periods=14, freq="D"),
"count": [120, 135, 142, 150, 160, 170, 165, 180, 190, 200, 210, 205, 220, 230]
})
# 이전 7일을 feature, 다음 하루를 label로 생성
X = []
y = []
for i in range(len(df) - 7):
X.append(df["count"].iloc[i:i+7].values) # 최근 7일 데이터
y.append(df["count"].iloc[i+7]) # 8일째 데이터
X = pd.DataFrame(X, columns=[
"day1", "day2", "day3", "day4", "day5", "day6", "day7"
])
y = pd.Series(y, name="target")
print("Feature (X)")
print(X)
print("\nLabel (y)")
print(y)
Feature (X)
day1 day2 day3 day4 day5 day6 day7
0 120 135 142 150 160 170 165
1 135 142 150 160 170 165 180
2 142 150 160 170 165 180 190
...
Label (y)
0 180
1 190
2 200
...
날씨 수준에 따라 행렬을 원핫인코딩으로 변환하고 unsqueeze를 사용해 싱글톤 차원을 더해 원래 데이터 셋에 cat함수을 사ㅛㅇ해 병합한다. cat으로 붙이기 위해서는 텐서에 붙을 차원의 크기가 같아야한다. 값의 범위를 변경하는 방법은 다양하고 값의 범위를 0.0, 1.0 으로 매핑하거나 모든 값에서 평균을 빼고 표준편차로 나눠 평균이 0이고 단위 표준 편차를 가지는 값으로 바뀐다. 가우시안 분포로 그리면 샘플의 68%은 -1.0, 1.0에 분포하게된다.
import torch
import torch.nn.functional as F
# 원본 데이터 (온도, 습도)
data = torch.tensor([
[25.0, 60.0],
[30.0, 50.0],
[20.0, 80.0],
[28.0, 55.0]
])
# 날씨 수준 (0: 맑음, 1: 흐림, 2: 비)
weather = torch.tensor([0, 1, 2, 0])
# 원-핫 인코딩
weather_onehot = F.one_hot(weather, num_classes=3)
# 원래 데이터와 병합
data = torch.cat((data, weather_onehot), dim=1)
print("원-핫 인코딩 후")
print(data)
# -------------------------------
# Min-Max 정규화 (0 ~ 1)
temp = data[:, 0].float()
temp_minmax = (temp - temp.min()) / (temp.max() - temp.min())
print("\nMin-Max 정규화")
print(temp_minmax)
# -------------------------------
# 표준화 (평균 0, 표준편차 1)
temp_standard = (temp - temp.mean()) / temp.std()
print("\n표준화")
print(temp_standard)
딥러닝은 NLP 분야도 폭풍처럼 강타했다. 특별히 모델의 이전 출력과 현재입력을 섞는 식으로 반복해 소비하는 형태의 모델이 대표적이며 이런 모델을 순환신경망 RNN이라고 부르는데 텍스트 분류와 생성, 자동번역 시스템에서 뛰어난 성공을 거줬다. 최근에는 Transformer라 불리는 신경망으로 과거의 정보를 포함하는 유연한 방법을 통해 큰성공을 거두고있다. 기존의 NLP는 언어 문법을 인코딩한 규칙이 들어있는 데이터로부터 훈련을 통해 유추되도록 다량의 말뭉치만 가지고 처음부터 끝까지 신경망을 훈련시키나 최근 수년동안 자동번역 서비스는 대부분 딥러닝으로 동작한다.
import torch
import torch.nn as nn
# 문장 → 토큰 (간단 예시)
sentence = ["딥러닝은", "NLP", "분야도", "강타했다"]
# vocab 생성
vocab = {w: i for i, w in enumerate(sentence)}
seq = torch.tensor([vocab[w] for w in sentence])
# -----------------------------
# 1. RNN 예시
embedding_dim = 8
hidden_dim = 16
embedding = nn.Embedding(len(vocab), embedding_dim)
rnn = nn.RNN(input_size=embedding_dim, hidden_size=hidden_dim, batch_first=True)
x = embedding(seq).unsqueeze(0) # (batch, seq, embed)
out, h = rnn(x)
print("RNN output shape:", out.shape)
print("RNN hidden shape:", h.shape)
# -----------------------------
# 2. Transformer Encoder 예시
encoder_layer = nn.TransformerEncoderLayer(
d_model=embedding_dim,
nhead=2,
batch_first=True
)
transformer = nn.TransformerEncoder(encoder_layer, num_layers=2)
x2 = embedding(seq).unsqueeze(0)
t_out = transformer(x2)
print("\nTransformer output shape:", t_out.shape)
신경망이 이해할 수 있는 표현으로 문장을 원핫 인코딩하고 단어 단위 인코딩같은 단어로 사전을 만들어 시퀀스에 대해 한단어 를 한 행으로 훤핫인코딩할경우 사전에 포함되는 단어가 매우 많아 인코딩 벡터가 매우 길어지면 실용성이 떨어짐으로 임베딩을 사용하여 단어단위로 텍스트를 표현한다.
import torch
import torch.nn as nn
import torch.nn.functional as F
# 문장 (단어 단위)
sentence = ["I", "love", "deep", "learning"]
# 단어 사전 생성
vocab = {word: i for i, word in enumerate(sentence)}
indices = torch.tensor([vocab[w] for w in sentence])
# -----------------------------
# 1. One-hot encoding
onehot = F.one_hot(indices, num_classes=len(vocab)).float()
print("One-hot encoding")
print(onehot)
print("shape:", onehot.shape)
# -----------------------------
# 2. Embedding layer
embedding_dim = 3
embed = nn.Embedding(num_embeddings=len(vocab), embedding_dim=embedding_dim)
embedded = embed(indices)
print("\nEmbedding output")
print(embedded)
print("shape:", embedded.shape)
문자레벨과 단어레벨 인코딩은 장단점이 있고 대부분의 언어는 단어수보다 문자수가 훨씬 적어 문자를 표현해 사용하면 표현할 수 있는 클래스도 몇개 되지않는다. 단어는 개별 문자보다 더 많은 의미를 내포하므로 단어 표현은 자체적으로 훨씬 많은 정보를 가지게 되니 이를 보완하는 방식이 발견되고 응용된사실은 그다지 놀랍지않다. 대부분의 경우 매핑은 단어 단위로 구분하나 Impossible이나 Bennet 과 같이 대문자로 시작하는 경우 세부 단위로 나누기도 한다.
import torch
import torch.nn.functional as F
# 문장 (단어 레벨)
sentence = ["Impossible", "Bennet", "is", "reading"]
# 단어 사전 (word-level vocab)
vocab = {word: i for i, word in enumerate(sentence)}
# 단어를 index로 변환
indices = torch.tensor([vocab[w] for w in sentence])
# -----------------------------
# Word-level one-hot encoding
onehot_word = F.one_hot(indices, num_classes=len(vocab)).float()
print("Word-level one-hot")
print(onehot_word)
# -----------------------------
# Character-level encoding
chars = sorted(list(set("".join(sentence))))
char_vocab = {c: i for i, c in enumerate(chars)}
char_indices = [[char_vocab[c] for c in word] for word in sentence]
print("\nCharacter-level indices")
print(char_indices)
# -----------------------------
# Subword-like split (simple rule: lowercase split + prefix)
def simple_subword(word):
return [word[:3], word[3:]] if len(word) > 3 else [word]
subwords = [simple_subword(w.lower()) for w in sentence]
sub_vocab = {s: i for i, s in enumerate({s for w in subwords for s in w})}
sub_indices = [[sub_vocab[s] for s in w] for w in subwords]
print("\nSubword representation")
print(sub_indices)
인코딩 크기를 처리할만한 규모로 줄이고 더이상 늘어나지 않게 하기 위해 부동소수점 수를 가지는 벡터를 사용하고 100개의 부동소수점을 가진 벡터라면 엄청나게 많은 수의 단어을 표현할 수 있다. 이러한 방법을 임베딩이라고 부른다. 임베딩에서 비슷한 단어들끼리 군집할 뿐아니라 일관된 공간관게를 유지한다. 단어의 임베딩 백터를 찾아 다른 단어를 더하거나 빼서 비유관계를 잡아낼 수도 있다. 사과-붉은-달콤한+노란+새콤한 처럼 계산하면 레몬벡터와 상당히 유사해진다. BERT나 GPT-2를 사용한 현대식 임베딩 모델은 더 정교하고 문맥에 민감하다. 사전 단어 매핑이 항상 고정되지않고 주변을 둘러싼 문장에서 영향을 받아 우리가 다루는 전형적 방식의 임베딩 처럼 사용할 수 있다. 어휘 집합내의 많은 개체가 숫자 벡터로 표현되어야 할 경우 임베딩은 필수적이다.
import torch
import torch.nn as nn
import torch.nn.functional as F
# 단어 예시
words = ["사과", "레몬", "붉은", "노란", "달콤한", "새콤한"]
vocab = {w: i for i, w in enumerate(words)}
# 임베딩 벡터 (100차원이라고 가정)
embedding_dim = 100
embedding = nn.Embedding(len(vocab), embedding_dim)
# 단어 인덱스
apple = torch.tensor(vocab["사과"])
red = torch.tensor(vocab["붉은"])
sweet = torch.tensor(vocab["달콤한"])
yellow = torch.tensor(vocab["노란"])
sour = torch.tensor(vocab["새콤한"])
lemon = torch.tensor(vocab["레몬"])
# -----------------------------
# 임베딩 벡터
apple_vec = embedding(apple)
red_vec = embedding(red)
sweet_vec = embedding(sweet)
yellow_vec = embedding(yellow)
sour_vec = embedding(sour)
lemon_vec = embedding(lemon)
# -----------------------------
# 벡터 연산 (비유 관계)
result = apple_vec - red_vec - sweet_vec + yellow_vec + sour_vec
# cosine similarity
cos_sim = F.cosine_similarity(result, lemon_vec, dim=0)
print("유사도:", cos_sim.item())
print("결과 벡터 shape:", result.shape)
print("레몬 벡터 shape:", lemon_vec.shape)
# -----------------------------
# BERT / GPT-style contextual embedding (흉내)
context = torch.randn(1, 6, embedding_dim) # 문맥에 따라 달라지는 벡터
transform = nn.TransformerEncoderLayer(
d_model=embedding_dim,
nhead=4,
batch_first=True
)
contextual_model = nn.TransformerEncoder(transform, num_layers=2)
contextual_out = contextual_model(context)
print("\n문맥 기반 임베딩 shape:", contextual_out.shape)
'Personal > Book' 카테고리의 다른 글
| 파이토치 딥러닝 마스터 - 1부 - 6장 신경망을 활용한 데이터 적합 (1) | 2026.06.28 |
|---|---|
| 파이토치 딥러닝 마스터 - 1부 - 5장 학습기법 (0) | 2026.06.28 |
| 파이썬으로 따라해보는 딥러닝 AI프로젝트 실사례 (0) | 2026.06.26 |
| 파이토치 딥러닝 마스터 - 1부 - 3장 텐서 구조체 (0) | 2026.06.23 |
| Do it! 자바 완전 정복 (0) | 2026.06.19 |