생물학적 뉴런이 신호를 받아 다음 뉴런으로 보내듯, 인공 뉴런도 입력을 받아 계산 후 출력한다.
같은 입력이라도 어떤 입력은 더 중요하게 반영되어야 한다.
딥러닝 학습의 본질은 결국,
좋은 예측을 만들도록 와 를 찾아가는 과정이다.
활성화 함수가 없으면 층을 아무리 많이 쌓아도 결국 전체가 “선형 함수”로 귀결된다.

그래서 현대 딥러닝은 은닉층에서 step을 거의 안 쓰고, 미분 가능한 함수로 넘어간다.
| 함수 | 출력 범위 | 장점 | 단점 |
|---|---|---|---|
| Sigmoid | 0 ~ 1 | 확률 해석 가능 | 기울기 소실 |
| ReLU | 0 ~ inf | 빠른 학습, 간단 | Dying ReLU |
| Tanh | -1 ~ 1 | 0 중심 | 기울기 소실 |
| Leaky ReLU | -inf ~ inf | Dying ReLU 해결 | alpha 튜닝 필요 |
예측값과 정답의 차이를 수치화한 것이 손실이고, 학습은 손실을 줄이는 방향으로 진행된다.
즉, 손실 함수는 x축이 w(weight)이고, y축이 Loss(손실)인 함수이고,
우리의 목적은 Loss가 최소가 되는 w(weight)를 찾는 것이다.
퍼셉트론은 “입력 × 가중치 → 합 → 활성화 → 출력” 구조다.

여기서 bias 는 “기준선(절편)”이다.
직관적으로는 “합산 점수가 이 정도는 넘어야 양성” 같은 기본 문턱값 역할.
퍼셉트론은 선형 분류기(직선/평면으로 나누는 모델)라는 점이 핵심이다.
이게 유명한 “단일 퍼셉트론의 한계”다.
XOR 같은 선형 분리 불가능 문제를 풀려면 여러 층(MLP) 이 필요하다.

결론: 층을 쌓는 이유 = 선형으로 못 푸는 문제를 비선형 변환으로 풀기 위해
퍼셉트론 학습 규칙은 이런 형태로 요약된다:

비유로 보면,
그래서 실전 가이드가 이렇게 정리된다:
ReLU가 도움이 되는 이유는 연산이 간단하여 속도가 빠르기도 하고,
양수 구간에서 기울기가 1로 유지되는 구간이 있어(포화가 덜해서)
깊은 층에서도 학습 신호가 비교적 잘 전달되기 때문이다.
PyTorch는 Python을 위한 오픈소스 머신 러닝 라이브러리이다.
Torch를 기반으로 하며, 자연어 처리와 같은 애플리케이션을 위해 사용된다.
GPU사용이 가능하기 때문에 속도가 상당히 빠르다. 이번 챕터에서 중요한 키워드는 다음 4개였다.
특히 Autograd는 PyTorch 학습의 심장이다.
Tensor는 “다차원 배열”이고, 차원별 의미를 감각적으로 잡는 게 중요하다.
PyTorch에서 Tensor 연산은 거의 NumPy처럼 진행된다(덧셈/곱셈/행렬곱/reshape 등).
관련 메서드들이 torch.Tensor에 정리되어 있다.
수업에서 강조된 포인트가 하나 있다.
torch.tensor(5) : 값 “5”를 담은 텐서를 만든다 (의도 명확)torch.Tensor(5) : 종종 “값 5”가 아니라 크기 5짜리(초기화되지 않은) 텐서로 동작할 수 있음실전에서는 torch.tensor()(소문자) 또는 torch.zeros, torch.ones, torch.randn 같은 함수를 쓰는 습관이 안전하다.
코드 예시
import torch
# -----------------------------------
방법1) 직접 생성
# -----------------------------------
# 스칼라 (0차원)
x = torch.tensor(5) # x = torch.Tensor(5)이거랑 다름 주의!!!
print(x) # tensor(5)
# 벡터 (1차원)
v = torch.tensor([1, 2, 3])
print(v) # tensor([1, 2, 3])
print(v.shape) # torch.Size([3])
# 행렬 (2차원)
m = torch.tensor([[1, 2, 3],
[4, 5, 6]])
print(m.shape) # torch.Size([2, 3])
# -----------------------------------
방법2) 특수 Tensor 생성
# -----------------------------------
# 모두 0으로 채운 Tensor
zeros = torch.zeros(3, 4) # 3x4 행렬
# 모두 1로 채운 Tensor
ones = torch.ones(2, 3) # 2x3 행렬
# 랜덤 값으로 채운 Tensor
rand = torch.rand(3, 3) # 0~1 사이 랜덤
# 정규분포 랜덤
randn = torch.randn(3, 3) # 평균 0, 표준편차 1
# -----------------------------------
방법3) NumPy에서 변환
# -----------------------------------
import numpy as np
# NumPy 배열 생성
np_array = np.array([1, 2, 3])
# PyTorch Tensor로 변환
tensor = torch.from_numpy(np_array)
# 다시 NumPy로 변환
back_to_np = tensor.numpy()
코드 예시
# -----------------------------------
1) 기본 연산
# -----------------------------------
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
# 덧셈
print(a + b) # tensor([5, 7, 9])
# 뺄셈
print(a - b) # tensor([-3, -3, -3])
# 곱셈 (원소별)
print(a * b) # tensor([4, 10, 18])
# 나눗셈
print(a / b) # tensor([0.25, 0.4, 0.5])
# -----------------------------------
2) 행렬 곱
# -----------------------------------
# 2x3 행렬
A = torch.tensor([[1, 2, 3],
[4, 5, 6]])
# 3x2 행렬
B = torch.tensor([[1, 2],
[3, 4],
[5, 6]])
# 행렬 곱 (2x3) @ (3x2) = (2x2)
C = A @ B # 또는 torch.matmul(A, B)
print(C.shape) # torch.Size([2, 2])
# -----------------------------------
3) Shape 변경
# -----------------------------------
x = torch.tensor([[1, 2, 3],
[4, 5, 6]]) # shape: (2, 3)
# Reshape
y = x.reshape(3, 2) # shape: (3, 2)
y = x.reshape(6) # shape: (6,) - 1차원으로
# View (메모리 공유)
z = x.view(3, 2)
# 차원 추가/제거
x_unsqueeze = x.unsqueeze(0) # shape: (1, 2, 3) # unsqueeze(dim) : 차원 추가 (예: 배치 차원 만들기)
x_squeeze = x_unsqueeze.squeeze(0) # shape: (2, 3) # squeeze(dim) : 크기가 1인 차원 제거
핵심은 “텐서가 있는 곳에서 연산이 수행된다”는 점이다.
GPU 사용 코드
# GPU 사용 가능 확인
print([torch.cuda.is](http://torch.cuda.is)_available()) # True/False
# 디바이스 설정
device = torch.device('cuda' if [torch.cuda.is](http://torch.cuda.is)_available() else 'cpu')
# Tensor를 GPU로 이동
x = torch.tensor([1, 2, 3])
x = [x.to](http://x.to)(device)
# 모델을 GPU로 이동
model = [model.to](http://model.to)(device)
모델이 cuda에 올라갔는지 확인할 땐 next(model.parameters()).is_cuda 같은 방식도 흔히 쓴다.
딥러닝 학습은 손실을 가중치로 미분해야 하는데, PyTorch는 그걸 Autograd가 자동으로 해준다.
requires_grad=True : “이 텐서에 대해 기울기 추적해줘”.backward() : 계산 그래프를 따라 역전파 수행x.grad : 저장Autograd가 “계산 그래프(computational graph)”를 만들고,
역전파로 gradient를 계산한다.
실전 학습은 거의 이 패턴으로 고정된다.
optimizer.zero_grad() 로 기울기 초기화)이 흐름을 기억하는게 가장 중요하다.
import torch
import torch.nn as nn
# 데이터 준비
X = torch.tensor([[1.], [2.], [3.], [4.]])
y = torch.tensor([[3.], [5.], [7.], [9.]]) # y = 2x + 1
# Step 1. 모델 정의 (Linear: y = wx + b)
model = nn.Linear(1, 1)
# Step 2.손실 함수 & 옵티마이저
criterion = nn.MSELoss() # 평균 제곱 오차
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# Step 3.학습 루프
for epoch in range(1000):
# Step 3-1.예측
pred = model(X)
# Step 3-2.손실 계산
loss = criterion(pred, y)
# Step 3-3.기울기 초기화 & 계산 & Step 3-4.업데이트
[optimizer.zero](http://optimizer.zero)_grad()
loss.backward()
optimizer.step()
# 학습된 가중치 확인
print(f"w: {model.weight.item():.2f}, b: {model.bias.item():.2f}")
# w: 2.00, b: 1.00 에 가까워짐!

perceptron() 이 > 0일 때 1을 리턴하는 구조이고, 마진(결정 경계(z=0)와의 거리)이 0.2여서 지금은 문제가 없음.perceptron()이 >= 0일 때 1을 리턴하는 구조라면, (1,1)에서 s가 정확히 0이 되는 값을 쓰면 애매해짐.return 1 if (w1*x1 + w2*x2) > b else 0 의 형태)
