import torch

x_train = torch.FloatTensor([[88],[95],[80],[100],[74]])
y_train = torch.FloatTensor([[80],[94],[85],[99],[73]])

# 모델 초기화
W = torch.zeros(1, requires_grad=True)
b = torch.zeros(1, requires_grad=True)

# 하이퍼파라미터 설정
lr = 0.01
nb_epochs = 100

for epoch in range(nb_epochs + 1):
    # 가설 계산
    hypothesis = x_train * W + b
    
    # cost 계산
    cost = torch.mean((hypothesis - y_train) ** 2)
    
    gradient_W = torch.sum((W * x_train - y_train) * x_train)
    
    W -= lr * gradient_W
   
# 실습문제2 [총 10점]
# 출제자: 송수연 # 검수자: 나머지
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

x_data = [[1, 2], [2, 6], [3, 1], [3, 4], [5, 3], [6, 2], [5, 4], [2, 1]]

(1)
y_data = [1 if sum(x) % 2 != 0 else 0 for x in x_data]

(2)

x_train = torch.FloatTensor(x_data).view(-1, 2)
y_train = torch.FloatTensor(y_data).view(-1, 1)

(3)
class BinaryClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear = nn.Linear(2, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, x):
        return self.sigmoid(self.linear(x))
    
model = BinaryClassifier()

(4)       
# optimizer 설정
optimizer = optim.SGD(model.parameters(), lr=0.01)

nb_epochs = 1000
for epoch in range(1, nb_epochs + 1):

    # H(x) 계산
    hypothesis = model(x_train)

    # cost 계산
    cost = F.binary_cross_entropy(hypothesis, y_train)

    # cost로 H(x) 개선
    optimizer.zero_grad()
    cost.backward()
    optimizer.step()
    
    # 2로그 출력
    if epoch % 100 == 0:
        print(f'Epoch [{epoch}/{nb_epochs}], Cost: {cost.item()}')
# 이론 문제
# 각 1점 [총 10점]
# 모두 O,X 문제 입니다.
# 10번 문제는 첨부한 수식을 참고해 풀어주세요.
1. [1점] nn.Linear(a,b)의 a는 입력 차원의 수, b는 출력 차원의 수입니다. (O)
2. [1점] 입력차원이 3개라면 3개의 가중치와 3개의 편향이 필요합니다. (X)

3. [1점] optimizer.step()은 기존의 기울기(gradient) 값을 초기화하는 단계에 사용되는 함수이다. (X)

4. [1점] Minibatch Gradient Descent는 전체 데이터를 균일하게 나누어서, 나눈 각각의 데이터를 하나하나 학습시키는 경사하강법을 의미한다. O

5. [1점] MSE는 오차 (실제값 - 예측값)들의 합이다. (X)
6. [1점] 학습하고자 하는 데이터의 양이 지나치게 방대한 경우 모든 데이터를 학습할 수는 없다. 
         이를 해결하고자 minibatch로 데이터를 균일하게 나누어 학습하는데 기존의 Batch gradient descent는 매끄럽게 감소하는 반면,
         minibatch gradient descent는 다소 거칠게 감소한다. (O)
7. [1점] optimizer = optim.SGD([W, b], lr=0.01) 에서 lr(learning rate)을 크게 설정할수록 W값을 더욱 빠르고 정확하게 찾을 수 있으므로 되도록 크게 설정하는것이 좋다. (X)

8. [1점] 다중 선형 회귀에서 x들의 수(샘플의 수 × 특성의 수)가 3X7의 경우 가중치를 W = torch.zeros((3, 1), requires_grad=True)로 선언해야 한다. (X)

9. [1점] 보통 Training Loss가 더이상 낮아지지 않을 때 Early Stopping을 하여 Overfitting을 방지한다.

# 위의 수식을 참고하세요. (O)
10. [1점] 위 수식에 의하면 hypothesis[0] = log P(x=1;W), (1 – hypothesis[0]) = log P(x=0;W)이다.
# 위의 수식 시그마(i=1부터 m) 중 i = 1인 경우에 해당합니다. (O)