Python_Deeplearning_pytorch_02(pytorch_linear_regression)
LinearRegression from Scratch
구현할 것
- 공부시간과 성적간의 관계를 모델링한다.
- 머신러닝 모델(모형)이란 수집한 데이터를 기반으로 입력값(Feature)와 출력값(Target)간의 관계를 하나의 공식으로 정의한 함수이다. 그 공식을 찾는 과정을 모델링이라고 한다.
- 이 예제에서는 공부한 시간으로 점수를 예측하는 모델을 정의한다.
- 입력값과 출력값 간의 관계를 정의할 수있는 다양한 함수(공식)이 있다. 여기에서는 딥러닝과 관계가 있는 Linear Regression 을 사용해본다. - 우리가 수집한 공부시간과 점수 데이터를 바탕으로 둘 간의 관계를 식으로 정의 할 수 있으면 내가 몇시간 공부하면 점수를 얼마 받을 수 있는지 예측할 수 있게 된다.
- 수집한 데이터를 기반으로 앞으로 예측할 수있는 모형을 만드는 것이 머신러닝 모델링이다.

학습(훈련) 데이터셋 만들기
- 모델을 학습시키기 위한 데이터셋을 구성한다.
- 입력데이터와 출력데이터를 따로 행렬로 구성한다.
- 같은 데이터 포인트의 입력, 출력 데이터를 같은 index에 정의한다.
모델링
모델 정의
- Feature와 Target간의 관계를 수식으로 정의한다.
- 여기서는 공부시간(Feature)와 점수(Target)간의 관계를 정의하는데 선형회귀(Linear Regression) 모델을 가설로 세우고 모델링을 한다.
- 많은 머신러닝 연구자들이 다양한 종류의 데이터에 관계를 예측할 수 있는 여러 알고리즘을 연구했다.
- 선형회귀 모델은 입력데이터와 출력데이터가 선형관계(비례 또는 반비례 관계)일때 좋은 성능을 나타낸다.
가설
- 아직은 이 식이 맞는지 틀린지는 알 수없기 때문에 이 식을 가설(hypothesis) 라고 한다.
- 가설을 세우고 모델링을 한 뒤 검증을 해서 좋은 예측결과를 내면 그 가설을 최종 결과 모델로 결정한다. 예측결과가 좋지 않을 경우 새로운 가설로 모델링을 한다.
선형회귀 (Linear Regression)
- Feature들의 가중합을 이용해 Target을 추정한다.
- Feature에 곱해지는 가중치(weight)들은 각 Feature가 Target에 얼마나 영향을 주는지 영향도가 된다.
- 음수일 경우는 target값을 줄이고 양수일 경우는 target값을 늘린다.
- 가중치가 0에 가까울 수록 target에 영향을 주지 않는 feature이고 0에서 멀수록 target에 많은 영향을 준다. - 모델 학습과정에서 가장 적절한 Feature의 가중치를 찾아야 한다.

경사하강법을 이용한 최적화
train data 정의
import torch
# 훈련(train) 데이터 정의
## 입력데이터 변수명: X, 출력데이터 변수명: y
X_train = torch.tensor([[1],
[2],
[3]
], dtype=torch.float32)
y_train = torch.tensor([[20],
[40],
[60]
], dtype=torch.float32)
X_train, y_train의 shape
print(X_train.shape, y_train.shape)
# [3, 1] -> [데이터개수, 개별데이터의 shape]
# => 3개의 데이터. 각데이터는 1개의 값을 구성된 1차원배열
모델 정의
- weight : 가중치
- bias : 편향
##### 모델 정의
torch.manual_seed(0)
### weight(가중치)와 bias(편향)를 정의
weight = torch.randn(1, 1, requires_grad=True) # (1: 입력 값의 개수, 1: 출력 값의 개수)
bias = torch.randn(1, requires_grad=True)
print("초기 파라미터")
print("weight:", weight)
print("bias", bias)
def linear_model(X):
pred = X @ weight + bias
return pred
pred -> predict를 의미
X_train 데이터를 model에 적용시켜 예측값 확인
### 예측
pred_train = linear_model(X_train)
pred_train
오차함수(mes_loss_fn) 정의
#### 오차함수 정의 -> 모델이 추론한 값과 정답사이의 차이를 계산 하는 함수.
# 평균 제곱 오차 (Mean Squared Error : MSE)
def mse_loss_fn(pred:"예측값", y:"정답"):
return torch.mean((pred - y)**2)
오차함수를 이용한 오차 확인
loss = mse_loss_fn(pred_train, y_train)
print('오차:', loss)
grad 계산
- grad -> gradient(경사)
#### weight 와 bias에 대한 gradient(경사) 계산
loss.backward()
weight와 bias에 대한 grad
print("weight의 grad::", weight.grad)
print("bias의 grad:", bias.grad)
경사하강법 연산으로 인해 weight(가중치)의 값이 커졌다. -> target에 더 많은 영향을 주도록 최적화되었다.
### 최적화 -> 경사하강법 ==> 파이토치의 함수를 사용.
optimizer = torch.optim.SGD([weight, bias], #최적화할 대상 => requires_grad=True
lr=0.0001 # 학습률
)
## 경사하강법 연산.
print("update전 weight:", weight)
optimizer.step()
print("update후 weight:", weight)
optimizer.zero_grad()
업데이트 이전과 이후의 loss값의 차이를 확인
- 업데이트 이후 loss(오차)의 값이 작아졌다.
#### 업데이트후 로스계산
## 추론
pred = linear_model(X_train)
## 오차 계산
loss2 = mse_loss_fn(pred, y_train)
print("이전:", loss)
print("업데이트후", loss2)
학습
- 1. 모델을 이용해 추정한다.
- pred = model(input) - 2. loss를 계산한다.
- loss = loss_fn(pred, target) - 3. 계산된 loss를 파라미터에 대해 미분하여 계산한 gradient 값을 각 파라미터에 저장한다.
- loss.backward() - 4. optimizer를 이용해 파라미터를 update한다.
- optimizer.step() - 5. 파라미터의 gradient(미분값)을 0으로 초기화한다.
- optimizer.zero_grad()
- 위의 단계를 반복한다.
학습 코드
STEPS = 500 # 파라미터(WEIGHT, BIAS)를 업데이트할 횟수.
for _ in range(STEPS):
# 1. 추론(예측)
pred = linear_model(X_train)
# 2. 오차 계산
loss = mse_loss_fn(pred, y_train)
# 3. weight와 bias에대한 gradient를 계산.
loss.backward()
# 4. 최적화 -> optimizer를 이용. ==> 경사하강법
optimizer.step()
# 5. 계산된 gradient값을 초기화
optimizer.zero_grad()
### weight, bias 값
print("weight:", weight)
print("bias:", bias)
오차 계산
### 오차 계산
pred3 = linear_model(X_train)
loss3 = mse_loss_fn(pred3, y_train)
print(loss3)
다중 입력, 다중 출력
- 다중입력: Feature가 여러개인 경우
- 다중출력: Output 결과가 여러개인 경우
다음 가상 데이터를 이용해 사과와 오렌지 수확량을 예측하는 선형회귀 모델을 정의한다.

사과수확량 = w11 * 온도 + w12 * 강수량 + w13 * 습도 + b1
오렌지수확량 = w21 * 온도 + w22 * 강수량 + w23 *습도 + b2
- 온도, 강수량, 습도 값이 사과와, 오렌지 수확량에 어느정도 영향을 주는지 가중치를 찾는다.
- 모델은 사과의 수확량, 오렌지의 수확량 두개의 예측결과를 출력해야 한다.
- 사과에 대해 예측하기 위한 weight 3개와 오렌지에 대해 예측하기 위한 weight 3개 이렇게 두 묶음, 총 6개의 weight를 정의하고 학습을 통해 가장 적당한 값을 찾는다.
- 이 묶음을 딥러닝에서는 Node, Unit, Neuron 이라고 한다. - 목적은 우리가 수집한 train 데이터셋을 이용해 정확한 예측을 위한 weight와 bias를 찾는 것이다.
Training Data
- Train data는 feature와 target를 각각 따로 2개의 행렬로 구성한다.
- Feature의 행은 관측치(개별 데이터)를 열을 Feature(특성, 변수)를 표현한다.
- Target은 모델이 예측할 대상으로 행은 개별 관측치, 열은 각 항목에 대한 정답으로 이 예제에서는 사과수확량과 오렌지 수확량 값을 가진다.
Train feature data
# Input (temp, rainfall, humidity) : (5, 3)
inputs = torch.tensor([[73, 67, 43],
[91, 88, 64],
[87, 134, 58],
[102, 43, 37],
[69, 96, 70]], dtype=torch.float32)
Train targets data
# Targets: 생산량 - (apples, oranges) - (5, 2)
targets = torch.tensor([[56, 70],
[81, 101],
[119, 133],
[22, 37],
[103, 119]], dtype=torch.float32)
Linear Regression Model (from scratch)
weight와 bias
- weight 는 각 feature에 곱해지는 가중치로 target 값에 얼마나 영향을 주는지를 나타낸다. 직선의 방정식에서 기울기이다.
- bias는 각 feature와 weight간의 가중합에 더해주는 값으로 모든 feature가 0일 경우 target의 값을 나타낸다. 직선의 방정식에서 절편이다.
- weight와 bias는 각각 random 값을 초기값으로 가지는 matrix로 정의한다.
- weight의 shape: (2, 3)
- bias의 shape: (2, )
import torch
torch.manual_seed(0)
weight(기울기), bias(절편) 생성
# weight와 bias를 생성
# 파라미터 -> 학습을 통해 찾아야 하는 값 -> requires_grad = True
weights = torch.randn(3, 2, requires_grad=True)
# [3, 2] -> [input feature개수, output개수] -> [[온도w, 강수량w, 습도w], [사과, 오렌지]]
bias = torch.randn(2, requires_grad=True) # [2] ->: [output개수 -> 사과, 오렌지]
weight, bias 정보 확인
print(weights.shape, bias.shape)
print(weights)
print(bias)
Linear Regression model
- 모델은 weights `w`와 inputs `x`의 내적(dot product)한 값에 bias `b`를 더하는 함수이다.

함수 정의
def model(X):
return X @ weights + bias
pred : 예측값
model : 위 코드에서 정의한 함수
inputs : Train feature data
# inputs.shape
pred = model(inputs)
결과값 확인
print(pred.shape)
pred
Loss Function
- 모델이 예측한 값과 정답간의 차이를 비교하는 메소드.
Loss function 선언
def mse_loss_fn(preds, targets):
squared_error = (preds - targets) ** 2
return torch.mean(squared_error)
예측값(pred)와 실제값(targets)의 차이 확인
loss = mse_loss_fn(pred, targets) # (2, ) -> 0: 사과생산량 오차, 1: 오렌지생산량 오차
loss
Gradients 계산
- loss에 대한 weight와 bias의 gradients (미분계수)를 계산한다. Pytorch의 자동미분을 이용한다. (graident를 구하려는 tensor는 requires_grad=True로 설정한다.)
loss에 대한 weight와 bias의 gradients (미분계수)를 계산
loss.backward()
print(weights)
print(weights.grad)
print(bias)
print(bias.grad)
모델 최적화
- gradient decent 알고리즘을 이용해 loss를 줄여 모델의 추론 성능을 높인다. 이를 위해 좋은 성능을 낼 수 있도록 경사하강법(gradient decent) 을 이용해 weight와 bias를 update한다.
1. 추론하기
2. loss 계산하기
3. weight와 bias에 대한 gradient계산하기
4. 계산된 gradient에 비례한 값을 학습률을 곱해 작게 만든 뒤 wegith에서 빼서 조정한다.
5. gradient를 0으로 초기화
step, lr, optimizer
# step(파라미터 업데이트)수 지정
STEPS = 100
# 학습률 (Learning Rate)
LR = 0.0001 #1e-4
# Optimizer 생성
optimizer = torch.optim.SGD([weights, bias], lr=LR) # 경사하강법처리. gradient값 초기화.
10번의 업데이트마다 오차를 출력
- 초반의 10번의 텀마다 오차가 크게 줄어드는 것을 확인할 수 있다.
for i in range(STEPS):
# 추론
preds = model(inputs)
# 오차계산
loss = mse_loss_fn(preds, targets)
# gradient 계산
loss.backward()
# 파라미터(weights, bias) 값들 업데이트
optimizer.step()
# 파라미터의 grad값 초기화
optimizer.zero_grad()
# 10 step당 loss를 출력
if i % 10 == 0 or i == (STEPS - 1):
print(f"Step: {i} - loss: {loss.item()} ") # tensor객체.item() : scalar나 원소가 1개인 tensor의 값을 추출(파이썬 타입 값)
weights와 bias 파라미터값의 차이 확인(위의 최적화 코드실행 전/후로 한번씩 실행하여 차이를 확인한다.)
print("학습전 또는 후의 파라미터") # 위의 모델 최적화 코드 실행 전, 후로 한번씩 실행 -> 결과확인
print(weights)
print(bias)
예측결과값 pred_value
pred_value = model(inputs)
예측결과값 pred_value와 실제값 targets의 값 차이 확인
- 최적화 코드를 반복 실행할 때마다 차이가 크게 줄어든다.
print(pred_value)
targets
pytorch built-in 모델을 사용해 Linear Regression 구현
똑같이 필요한 import와 inputs데이터, tragets데이터 준비
import torch
import torch.nn as nn # 모델들을 제공하는 모듈
inputs = torch.tensor([[73, 67, 43],
[91, 88, 64],
[87, 134, 58],
[102, 43, 37],
[69, 96, 70]], dtype=torch.float32)
targets = torch.tensor([[56, 70],
[81, 101],
[119, 133],
[22, 37],
[103, 119]], dtype=torch.float32)
nn.Linear
- Pytorch는 nn.Linear 클래스를 통해 Linear Regression 모델을 제공한다.
- nn.Linear에 입력 feature의 개수와 출력 값의 개수를 지정하면 random 값으로 초기화한 weight와 bias들을 생성해 모델을 구성한다.
nn.Linear()함수를 통해 weight(가중치), bias(편향) 생성 및 조회
# Linear(input feature개수, output개수) # 모델 정의
model = nn.Linear(3, 2)
# weight와 bias를 조회
print(model.weight)
print(model.bias)
Optimizer와 Loss 함수 정의
- torch.optim 모듈에 다양한 Optimizer 클래스가 구현되있다. 그 중에서 Adam를 사용한다.
- torch.nn 또는 torch.nn.functional 모듈에 다양한 Loss 함수가 제공된다. 이중 mse_loss() 를 사용한다.
nn.MSELoss() -> 객체
torch.nn.functional.mse_loss() -> 함수
# Loss 함수 정의
# nn.MSELoss()객체 사용 or torch.nn.functional.mse_loss()함수 사용
loss_fn = nn.MSELoss()
optimizer 정의
# optimizer
optimizer = torch.optim.SGD(model.parameters(), lr = 0.0001) # ([최적화대상-모델의 파라미터], lr = 학습률)
학습전 추론
# 학습전에 추론 -> 오차 계산
# 추론
pred = model(inputs)
pred
학습전 추론 결과와 실제값의 오차 계산
# 오차 계산
loss = loss_fn(pred, targets) # (모델예측결과, 정답)
loss
Model Train
- 주어진 epoch 만큼 학습하는 fit 함수를 정의한다.
train 함수 코드
def fit(num_epochs:"몇번 반복할지", model:"학습시킬모델", loss_fn:"오차함수", optim:"옵티마이저", inputs:"입력데이터", targets:"출력데이터"):
for epoch in range(num_epochs):
# 1. 추론
pred = model(inputs)
# 2. 오차 계산
loss = loss_fn(pred, targets)
# 3. gradient 계산
loss.backward()
# 4. parameter update
optim.step()
# 5. 계산된 gradient값 초기화
optim.zero_grad()
# loss(결과) 출력
if epoch % 10 == 0 or epoch == (num_epochs-1):
print(f"{epoch}/{num_epochs}: train loss: {loss.item():.5f}")
경사하강법의 최적화 코드와 비슷하게 초반에 오차가 크게 줄어드는 것을 확인할 수 있다.
fit(100, model, loss_fn, optimizer, inputs, targets)
학습후의 모델 추론 결과 및 실제값 확인 -> 차이가 크지 않다.
# 학습후 모델 추론 결과 확인
p = model(inputs)
print(p)
print(targets)
학습후 모델 추론 결과와 실제값의 차이 확인
l = loss_fn(p, targets)
l.item()