<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>LeeCreation! Media &amp;amp; Robot</title>
    <link>https://leestation.tistory.com/</link>
    <description>미디어와 로봇에 관심이 많은 아이 그 영역을 넓혀보려 합니다.
 '영상 제작'과 '감정 로봇'이 블로그의 주소재입니다. 자유로운 답글 환영합니다!</description>
    <language>ko</language>
    <pubDate>Tue, 26 May 2026 06:20:06 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>이원형</managingEditor>
    <image>
      <title>LeeCreation! Media &amp;amp; Robot</title>
      <url>https://t1.daumcdn.net/cfile/tistory/1878AC0E49C338A301</url>
      <link>https://leestation.tistory.com</link>
    </image>
    <item>
      <title>PyTorch로 CNN 만들기</title>
      <link>https://leestation.tistory.com/795</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터는 역시나 MNIST&lt;/p&gt;
&lt;pre id=&quot;code_1759842721033&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 필요한 라이브러리들을 불러오고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 데이터셋의 형식과 모양을 바꿔야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1759842891499&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 1. ToTensor()만 적용하는 Transform
# 정규화(Normalize)는 제외하고 [0.0, 1.0]으로 스케일링만 합니다.
transform_only_to_tensor = transforms.Compose([
    transforms.ToTensor() 
])

# 2. 데이터셋 로드 (ToTensor만 적용된 상태)
train_dataset = datasets.MNIST('./data', train=True, download=True, transform=transform_only_to_tensor)
test_dataset = datasets.MNIST('./data', train=False, transform=transform_only_to_tensor)

# 3. DataLoader 설정
BATCH_SIZE = 64
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=1000, shuffle=False)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759843357628&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;print(train_dataset)
print(test_dataset)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;232&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNdahc/btsQ2bqwj5g/U8l2oc953FYtPo3WNS5YR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNdahc/btsQ2bqwj5g/U8l2oc953FYtPo3WNS5YR1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNdahc/btsQ2bqwj5g/U8l2oc953FYtPo3WNS5YR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNdahc%2FbtsQ2bqwj5g%2FU8l2oc953FYtPo3WNS5YR1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;232&quot; height=&quot;276&quot; data-origin-width=&quot;232&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 데이터는 다운 받아서 왔고, 학습에 6만개, 테스트에 1만개인 것은 알겠는데, numpy에서 보던 (60000, 28, 28) 뭐 이런 식의 shape이 아니라서 알 수가 없다. 게다가 batch 크기로 나누어 놓은 loader의 경우도 데이터의 크기를 바로 알 수 있는 형태가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Gemini한테 물어보니 iter() 함수를 통해 iteration 반복자로 변환한 후 next() 함수로 불러오는 식으로 하면 된다더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759845951705&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 2. DataLoader에서 첫 번째 배치 가져오기
# iter()로 반복자(iterator)를 만든 후 next()를 호출합니다.
data_iter = iter(train_loader)
images, labels = next(data_iter) # images: 이미지 텐서, labels: 레이블 텐서

# 3. 속성 확인 및 출력
print(&quot;\n--- DataLoader를 통해 얻은 배치(Batch) 속성 ---&quot;)
print(f&quot;이미지 텐서 모양 (Image Tensor Shape): {images.shape}&quot;)
print(f&quot;이미지 텐서 자료형 (Image Tensor Data Type): {images.dtype}&quot;)
print(&quot;-&quot; * 30)
print(f&quot;레이블 텐서 모양 (Label Tensor Shape): {labels.shape}&quot;)
print(f&quot;레이블 텐서 자료형 (Label Tensor Data Type): {labels.dtype}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oGosk/btsQ35im0JG/NRJKhLJYHTRxwYiKxcwEwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oGosk/btsQ35im0JG/NRJKhLJYHTRxwYiKxcwEwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oGosk/btsQ35im0JG/NRJKhLJYHTRxwYiKxcwEwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoGosk%2FbtsQ35im0JG%2FNRJKhLJYHTRxwYiKxcwEwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;120&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 첫 batch에 있는 자료를 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Keras 때는 channel 정보를 shape의 마지막에 넣어두었었는데, 여기서는 그게 먼저 오는 것 같고, 값을 하나씩 보니, 이미지는 각 픽셀이 0에서 1 사이 값으로, label은 0, 1, 2, ..., 9 이렇게 들어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이제 그냥 해도 되긴 하는데, 예시 코드를 보니 평균과 표준편차를 이용해서 normalize를 하더라. 계산해야 겠지만, 이미 잘 알려져 있는 데이터이므로, 그 값은, MEAN = &lt;span&gt;0.1307;&lt;/span&gt; STD = &lt;span&gt;0.3081 이라고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이제 CNN 구조를 만들어 보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759846252077&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CNNModel(nn.Module):
    def __init__(self):
        super().__init__()
        
        # 1. 컨볼루션 계층 1 (Convolutional Layer 1)
        # 입력 채널(Input Channel): 1 (흑백 이미지)
        # 출력 채널(Output Channel): 32
        # 커널 크기(Kernel Size): 3x3
        self.conv1 = nn.Conv2d(1, 32, 3) 
        
        # 2. 컨볼루션 계층 2 (Convolutional Layer 2)
        # 입력 채널: 32
        # 출력 채널: 64
        self.conv2 = nn.Conv2d(32, 64, 3) 
        
        # 3. 풀링 계층 (Pooling Layer) - Max Pooling
        # nn.MaxPool2d는 보통 forward()에서 F.max_pool2d로 사용됩니다.
        
        # 4. 드롭아웃 (Dropout)
        # 과적합(Overfitting) 방지를 위해 사용됩니다.
        self.dropout1 = nn.Dropout(0.25)
        
        # 5. 선형 계층 (Linear Layer)
        # Conv/Pooling을 거친 Flatten된 출력을 받아 10개의 클래스(0~9)로 분류합니다.
        # 입력 크기는 (28x28 이미지 기준) 9216 (Flatten된 크기)을 계산하여 얻어야 합니다. 
        self.fc1 = nn.Linear(9216, 128) # fc: Fully Connected (전결합)
        self.fc2 = nn.Linear(128, 10)   # 출력: 10개 클래스 (0~9)

    def forward(self, x):
        # 1. Conv1 -&amp;gt; ReLU -&amp;gt; Max Pooling
        x = self.conv1(x)
        x = F.relu(x)
        
        # 2. Conv2 -&amp;gt; ReLU -&amp;gt; Max Pooling (커널 크기 2x2)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, 2)
        
        # 3. 드롭아웃 적용
        x = self.dropout1(x)
        
        # 4. Flatten (평탄화): 2차원/3차원 데이터를 1차원 벡터로 만듦
        # x.shape: (배치 크기, 채널, 높이, 너비) -&amp;gt; (배치 크기, 채널*높이*너비)
        x = torch.flatten(x, 1) 
        
        # 5. Fully Connected Layers
        x = self.fc1(x)
        x = F.relu(x)
        
        x = self.fc2(x)
        
        # 최종 출력: 10개의 클래스에 대한 로짓(Logits)
        return x&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 위 코드에 주석으로 설명이 다 들어갔지만, 전체적인 설명을 하자면, 이전 글에서는 sequential하게 이미 __init__() 함수 내에서 layer 간의 연결이 다 이루어졌지만, 여기서는 __init__() 함수 안에서는 layer들만 정의가 되고, forward() 함수에서 모델 간의 관계가 정의되는 식이다. Keras에 익숙한 내가 보기에는 functional api 방식으로 모델을 만들 때와 비슷한 느낌이다. 다소 번거로울 수 있지만, 입출력 데이터의 모양을 명확하게 알아야 하는 부분이기도 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 모델도 만들었겠다, 장치 설정도 하고 손실함수와 옵티마이저도 정의하자.&lt;/p&gt;
&lt;pre id=&quot;code_1759846608902&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# GPU 사용 가능 여부를 확인하고 장치 설정
device = torch.device(&quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;)

# 모델 인스턴스 생성 및 GPU로 이동
model = CNNModel().to(device)

print(f&quot;모델은 {device}에서 실행됩니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759846631797&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 손실 함수(Loss Function): 분류 문제에 적합한 CrossEntropyLoss 사용
criterion = nn.CrossEntropyLoss()

# 옵티마이저(Optimizer): Adam 사용
optimizer = optim.Adam(model.parameters(), lr=0.001)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 학습 루프와 테스트 루프를 함수 형태로 제작하자. 그리고 원레 데이터 다운로드하면서 하는 게 일반적이지만, 본 글에서는 데이터를 normalize하지 않고 가져왔기 때문에, 여기서는 학습과 테스트 함수 내에서 normalize까지 함께 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759846765827&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;MEAN = 0.1307
STD = 0.3081

# 데이터 정규화(Normalization)를 수행하는 함수 정의
def normalize_data(data, mean, std):
    # (data - mean) / std 수식 적용
    return (data - mean) / std 

# ----------------- train 함수 수정 (손실만 반환) ------------------
def train(model, device, train_loader, optimizer, epoch, mean, std):
    model.train()
    total_loss = 0 # Epoch 전체 손실을 누적할 변수
    
    for batch_idx, (data, target) in enumerate(train_loader):
        data = normalize_data(data, mean, std)
        data, target = data.to(device), target.to(device)
        
        optimizer.zero_grad() 
        output = model(data)   
        loss = criterion(output, target) 
        loss.backward()        
        optimizer.step()
        
        total_loss += loss.item() * len(data) # 배치 크기를 곱해 실제 손실 기여도를 누적
        
        if batch_idx % 100 == 0:
            print(f'Train Epoch: {epoch} [{batch_idx * len(data)}/{len(train_loader.dataset)} ({100. * batch_idx / len(train_loader):.0f}%)]\tLoss: {loss.item():.6f}')
            
    # 에포크 전체 평균 손실 반환
    return total_loss / len(train_loader.dataset) 


# ----------------- evaluate_train 함수 추가 (정확도 계산) ------------------
def evaluate_train(model, device, train_loader, mean, std):
    model.eval()
    correct = 0
    
    with torch.no_grad():
        for data, target in train_loader:
            data = normalize_data(data, mean, std)
            data, target = data.to(device), target.to(device)
            output = model(data)
            
            pred = output.argmax(dim=1, keepdim=True)
            correct += pred.eq(target.view_as(pred)).sum().item()

    accuracy = 100. * correct / len(train_loader.dataset)
    # print(f'Train set: Accuracy: {correct}/{len(train_loader.dataset)} ({accuracy:.0f}%)')
    return accuracy
    
# ----------------- test 함수는 이전과 동일 (손실/정확도 모두 반환) ------------------
def test(model, device, test_loader, mean, std):
    model.eval() 
    test_loss = 0
    correct = 0
    
    with torch.no_grad():
        for data, target in test_loader:
            data = normalize_data(data, mean, std)
            data, target = data.to(device), target.to(device)
            output = model(data)
            
            test_loss += criterion(output, target).item() 
            pred = output.argmax(dim=1, keepdim=True)  
            correct += pred.eq(target.view_as(pred)).sum().item() 

    test_loss /= len(test_loader.dataset)

    print(f'\nTest set: Average loss: {test_loss:.4f}, Accuracy: {correct}/{len(test_loader.dataset)} ({100. * correct / len(test_loader.dataset):.0f}%)\n')
    return test_loss, 100. * correct / len(test_loader.dataset)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 학습을 실행해보자. normalize를 위해 mean과 std를 함수에 전달해줘야 한다.... 역시나 데이터 다운로드 할 때 함께 할 걸 그랬다. ㅎㅎ;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keras에서는 학습하라는 쉬운 명령어가 있었던 것 같은데 pytorch는 일일히 다 만들어줘야 하는 건가? 아니면 교육자료로 Gemini한테 만들어달라고 해서 굳이 디테일하게 보여주는 건가? ㅎㅎ 학습 history 기록도 챙겨줘야 하니 뭔가 좀 번거롭다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간을 절약하기 위해 epoch 수는 10번으로 실행&lt;/p&gt;
&lt;pre id=&quot;code_1759846809785&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;num_epochs = 10
train_loss_history = []      # 훈련 손실 기록
test_loss_history = []       # 테스트 손실 기록
train_accuracy_history = []  # 훈련 정확도 기록
test_accuracy_history = []   # 테스트 정확도 기록

print(&quot;CNN 모델 학습 및 결과 기록 시작...&quot;)

for epoch in range(1, num_epochs + 1):
    # 1. 훈련 및 훈련 손실 기록 (Training and Train Loss Recording)
    # train 함수는 이제 Epoch의 평균 손실을 반환합니다.
    avg_train_loss = train(model, device, train_loader, optimizer, epoch, MEAN, STD)
    train_loss_history.append(avg_train_loss)
    
    # 2. 훈련 정확도 계산 및 기록 (Train Accuracy Calculation)
    train_accuracy = evaluate_train(model, device, train_loader, MEAN, STD)
    train_accuracy_history.append(train_accuracy)
    
    # 3. 테스트 손실 및 정확도 계산 및 기록 (Test Loss &amp;amp; Accuracy Recording)
    avg_test_loss, test_accuracy = test(model, device, test_loader, MEAN, STD)
    test_loss_history.append(avg_test_loss)
    test_accuracy_history.append(test_accuracy)

print(&quot;CNN 모델 학습 완료!&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJpYJY/btsQ5aDjqPC/oOem7IMco6PabamvqSODQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJpYJY/btsQ5aDjqPC/oOem7IMco6PabamvqSODQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJpYJY/btsQ5aDjqPC/oOem7IMco6PabamvqSODQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJpYJY%2FbtsQ5aDjqPC%2FoOem7IMco6PabamvqSODQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;418&quot; height=&quot;263&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test loss가 0이라고??? 뭔가 이상한데;; 어쨌든 정확도가 엄청 높게 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프로 결과를 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759846986258&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ----------------------------------------------------
#               그래프 시각화 (Visualization)
# ----------------------------------------------------

# Epoch 인덱스 리스트 (X축)
epochs = range(1, num_epochs + 1)

plt.figure(figsize=(12, 5))


# A. 손실(Loss) 그래프: 훈련 손실 vs. 테스트 손실
plt.subplot(1, 2, 1)
plt.plot(epochs, train_loss_history, 'b-o', label='Training Loss') # 훈련 손실
plt.plot(epochs, test_loss_history, 'r-o', label='Test Loss')     # 테스트 손실
plt.title('Loss over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Loss (Cross Entropy)')
plt.grid(True)
plt.legend()

# B. 정확도(Accuracy) 그래프: 훈련 정확도 vs. 테스트 정확도
plt.subplot(1, 2, 2)
plt.plot(epochs, train_accuracy_history, 'b-o', label='Training Accuracy') # 훈련 정확도
plt.plot(epochs, test_accuracy_history, 'r-o', label='Test Accuracy')     # 테스트 정확도
plt.title('Accuracy over Epochs')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.grid(True)
plt.legend()

plt.tight_layout()
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mlI2Y/btsQ4pAX5jK/xGpgKHybZErteGtgOXNrP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mlI2Y/btsQ4pAX5jK/xGpgKHybZErteGtgOXNrP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mlI2Y/btsQ4pAX5jK/xGpgKHybZErteGtgOXNrP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmlI2Y%2FbtsQ4pAX5jK%2FxGpgKHybZErteGtgOXNrP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;326&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test loss는 뭔가 이상하지만 어쨌든 accuracy는 그럴듯하게 나오는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 test loss 계산식에 오류가 있었는 것 같은데... 그건 다음에 확인해보도록 하자;; (정신력 부족)&lt;/p&gt;</description>
      <category>Pytorch</category>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/795</guid>
      <comments>https://leestation.tistory.com/795#entry795comment</comments>
      <pubDate>Tue, 7 Oct 2025 23:38:44 +0900</pubDate>
    </item>
    <item>
      <title>세포 및 신호 전달</title>
      <link>https://leestation.tistory.com/794</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Disclaimer: &lt;a href=&quot;https://leestation.tistory.com/793&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leestation.tistory.com/793&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세포(Cell)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;유전자 정보가 담겨 실제로 작동하고 환경과 상호작용하는 물리적 공간의 가장 작은 작동 단위&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세포의 종류&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원핵세포(Prokaryote): 핵(Nucleus)이 따로 없는 단순한 구조의 세포 (예: 박테리아)&lt;/li&gt;
&lt;li&gt;진핵세포(Eukaryote): 핵, 미토콘드리아 등 막으로 둘러싸인 다양한 소기관(Organelle)을 가진 복잡한 세포&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세포의 구조 및 기능&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세로 내에는 고유한 기능을 수행하는 소기관(Organelle) 들이 있음&lt;/li&gt;
&lt;li&gt;핵(Nucleus): 유전자 정보의 저장 및 보호&lt;/li&gt;
&lt;li&gt;미토콘드리아(Mitochondria): 에너지 생성&lt;/li&gt;
&lt;li&gt;리보솜(Ribosome): 유전자 정보를 읽어 단백질을 합성&lt;/li&gt;
&lt;li&gt;세포막(Cell Membrane): 세포 내부와 외부를 구분하고 물질 출입 제어&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세포의 신호 전달&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세포가 외부 환경 또는 다른 세포로부터 온 자극을 감지하고 이 정보를 세포 내로 변환하여 궁극적으로 유전자 발현(이나 세포 활동을 변화시키는 과정&lt;/li&gt;
&lt;li&gt;신호는 일련의 분자적 사건을 거쳐 증폭되고 변환됨. 이를 신호 전달 경로라고 부름&lt;/li&gt;
&lt;li&gt;신호 전달 경로에 이상이 생기면 세포가 비정상적으로 증식하거나 사멸되지 않음.&lt;/li&gt;
&lt;li&gt;세포막에는 외부 신호를 감지하는 수용체(Receptor)라는 단백질이 존재. 외부 분자(Ligand)가 이 수용체에 결합하면, 세포 내부로 신호가 전달되어 특정 유전자가 켜지거나 끄지는 신호 전달 경로가 작동&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EdKrR/btsQ5Gvb8z1/mfnUqwB3jWVA4bEVYhcqS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EdKrR/btsQ5Gvb8z1/mfnUqwB3jWVA4bEVYhcqS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EdKrR/btsQ5Gvb8z1/mfnUqwB3jWVA4bEVYhcqS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEdKrR%2FbtsQ5Gvb8z1%2FmfnUqwB3jWVA4bEVYhcqS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1164&quot; height=&quot;644&quot; data-origin-width=&quot;1164&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;660&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8xXsj/btsQ4OUP10j/EHhh7p3BijTqHyr0mxpwHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8xXsj/btsQ4OUP10j/EHhh7p3BijTqHyr0mxpwHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8xXsj/btsQ4OUP10j/EHhh7p3BijTqHyr0mxpwHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8xXsj%2FbtsQ4OUP10j%2FEHhh7p3BijTqHyr0mxpwHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1172&quot; height=&quot;660&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;660&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처: &lt;a href=&quot;https://youtu.be/Z_H47hBhXCk?si=lqCUTt-RT3mlZigt&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/Z_H47hBhXCk?si=lqCUTt-RT3mlZigt&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 설명 영상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://youtu.be/ZFs7eQj90vk?si=Jp6oeqt0ArGrvnrs&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://youtu.be/ZFs7eQj90vk?si=Jp6oeqt0ArGrvnrs&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=ZFs7eQj90vk&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/hoD4r/hyZKN8hkPB/KCG7Zg4CZFTXpvkncdtDQ0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=18_642_62_690,https://scrap.kakaocdn.net/dn/f5qwd/hyZKNUKrWv/4dKTQGA8iYpcsXE03tO5S1/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=18_642_62_690&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;[고1 통합과학] 26강.동물 세포/식물 세포의 소기관 특징과 역할, 한 번에 정리해요!&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/ZFs7eQj90vk&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>공학자를위한 Bioengineering 입문</category>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/794</guid>
      <comments>https://leestation.tistory.com/794#entry794comment</comments>
      <pubDate>Tue, 7 Oct 2025 22:00:34 +0900</pubDate>
    </item>
    <item>
      <title>Disclaimer</title>
      <link>https://leestation.tistory.com/793</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;※ 본 챕터에서 작성되는 글들은 Gemini 2.5 Flash의 도움을 받아 작성되었습니다. 개인적인 학습을 위해 만들어진 자료로 일부 내용은 공학자적인 시각으로 재해석되어 학계의 정설과 내용이 다를 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;※&lt;span&gt; 본인은 전기및전자공학 학사, 석사, 박사를 마치고, 로봇 분야로 박사후연구원이었다가, 현재는 컴퓨터 공학 전공 교수로 활동 중입니다. 이러한 본인의 배경을 이해하고 글을 읽어주시기 바랍니다. (저 개인에 대해서는 따로 소개 페이지가 있습니다. (&lt;a href=&quot;https://sites.google.com/site/leestation/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>공학자를위한 Bioengineering 입문</category>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/793</guid>
      <comments>https://leestation.tistory.com/793#entry793comment</comments>
      <pubDate>Sat, 4 Oct 2025 00:44:43 +0900</pubDate>
    </item>
    <item>
      <title>Bioengineering이란? Bio와 Engineering</title>
      <link>https://leestation.tistory.com/792</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Disclaimer:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://leestation.tistory.com/793&quot;&gt;https://leestation.tistory.com/793&lt;/a&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bio (생물학, 생명과학)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생명 현상을 이해하고 관찰하는 것이 목표&lt;/li&gt;
&lt;li&gt;자연계에 존재하는 있는 그대로의 시스템을 설명하려는 학문&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bioengineering&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생물학적 지식을 바탕으로 측정, 조작, 설계하는 공학적 원리는 적용하는 것이 목표&lt;/li&gt;
&lt;li&gt;시스템을 분석, 개선, 재설계하여 인간에게 유용한 가치(진단, 치료, 생산)를 창출하는데 초점&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Bioengineering이 Engineering과 다른 점&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;생체 시스템은 기존 공학 시스템과 달리 수많은 피드백 루프와 변후가 얽힌 비선형적이고 확률적인 시스템&lt;/li&gt;
&lt;li&gt;동일한 유전자형(Genotype)을 가진 개체도 환경에 따라 다른 표현형(Phynotype)을 보일 수 있음&lt;/li&gt;
&lt;li&gt;이러한 복잡성과 불확실성을 수학적 모델링, 컴퓨터 시뮬레이션, 센서 및 계측 기술을 활용하여 정량화하고 제어하는 것이 Bioengineering의 핵심&lt;/li&gt;
&lt;li&gt;Biomedical Engineering, Genetic Engineering, Systems Biology 등의 세부 분야 등이 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적인 생각&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요즘 공학 시스템, 특히 인공지능의 경우 그 시스템의 크기와 복잡도가 상당히 커져가며 비선형적이고 확률적인 시스템적 특징을 가지고 있음. 따라서 생체 시스템만 복잡하다고 이야기하기에는 다소 무리가 있지 않나 싶음. 역으로 복잡한 인공 시스템을 구현하여 생체 시스템의 원리를 역으로 이해하는 방식도 괜찮은 접근방식일 수 있겠음.&lt;/li&gt;
&lt;li&gt;그러나 여전히 생체 시스템의 복잡도는 아직 알아야 할 것이 많다고 생각함. 위에서도 언급했듯이 유전자형과 표현형이 다소 간접적으로 연결되어 있기에 후성유전 같은 것을 알게 되면 이게 얼마나 신비롭고 복잡한 것인지 이해할 수도 있겠음. 삼체 문제도 풀지 못하는데 환경과 주변 개체들에 상호작용하는 특징을 이해하기에는 확률적 접근 말고는 아마도 그 메커니즘에 대한 이해가 쉽지 않아보임.&lt;/li&gt;
&lt;li&gt;그럼에도 인공지능을 통해 시뮬레이션해보고 새로운 관계를 이해해나가는 과정은 흥미로운 분야임. 단백질 구조를 밝히는 알파 폴드로 노벨상을 받기도 하였으니, 세상이 어떻게 바뀔지는 조금 더 지켜볼 문제.&lt;/li&gt;
&lt;li&gt;뭔가 나도 여기에 같이 숟가락을 얻고 싶지만... 여튼 열심히 공부해보자.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>공학자를위한 Bioengineering 입문</category>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/792</guid>
      <comments>https://leestation.tistory.com/792#entry792comment</comments>
      <pubDate>Sat, 4 Oct 2025 00:39:17 +0900</pubDate>
    </item>
    <item>
      <title>PyTorch로 기본 인공신경망 만들기</title>
      <link>https://leestation.tistory.com/791</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이제 인공신경만을 하나씩 만들어 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더불어서, 굳이 하나만 더 언급하자면, 우리가 만드는 모델이 분류(classification) 모델인지 예측(regression) 모델인지 따져볼 필요도 있겠다. (regression은 선형회귀라고 부르지만... 예측이라고 일단 하고 넘어가겠다.) 분류는 카테고리가 여러개인 것 중에 어떤 카테고리 하나가 선택되어야 하는지에 대한 문제이고, 예측은 어떤 값이 나와야 하는지에 대한 문제이다. 예를 들어서, 이미지를 바탕으로 고양이와 개라는 두 개의 카테고리를 두고 어느 것이 선택되어야 하는가에 대한 것이라면 분류 문제이고, 나이와 몸무게를 바탕으로 키가 얼마나 될지에 대한 값을 예측하는 것이라면 예측 문제다. (참고로, 예측할 값을 카테고리로 나누어서 분류 문제로 접근할 수도 있지는 하다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여튼 가장 처음 만들어볼 모델은 예측 모델이다. 앞서 들었던 나이와 몸무게를 바탕으로 키가 얼마나 될지를 예측하는 인공신경망을 만들어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선, 데이터를 만들어야 하는데, 어디선가 데이터를 불러올 수도 있겠지만, 여기서는 가상의 데이터를 만들도록 하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759495841714&quot; class=&quot;elm&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;import torch&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759496083030&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;X_features = torch.randn(100, 2) * 10 + torch.tensor([25.0, 70.0]) # 나이: 25세 근처, 몸무게: 70kg 근처&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;torch.randn(100, 2)함수로 shape이 (100, 2) (또는 torch.Size([100,2])인 torch tensor를 만들었다. 각각의 값은 0부터 1 사이의 임의의 값이고, 여기에 10씩 곱한 다음, 첫번째 열에는 25씩을, 두번째 열에는 70씩을 더했으니, X_features는 첫번째 열에는 평균이 25이고 편차가 10인 100개의 값이 들어있고, 두번째 열에는 평균이 70이고 편차가 10인 100개의 값이 들어있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;X_features는 모델에 입력으로 사용될 값들이고, 이제 출력으로 사용될 키에 대한 값들도 만들어 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키 = 나이*0.5 + 몸무게*1.5 + 오차&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 이 식을 기준으로 키에 대한 값을 만들도록 하겠다.&lt;/p&gt;
&lt;pre id=&quot;code_1759496352814&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;y_labels = (X_features[:, 0] * 0.5 + X_features[:, 1] * 1.5 + 50) + torch.randn(100) * 5
print(y_labels.shape)
y_labels = y_labels.unsqueeze(1) # 모양을 (100, 1)로 만듭니다.
print(y_labels.shape)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;239&quot; data-origin-height=&quot;66&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bioMH5/btsQ15XXl2V/EELSRkXSEcHtqHFSUgLKm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bioMH5/btsQ15XXl2V/EELSRkXSEcHtqHFSUgLKm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bioMH5/btsQ15XXl2V/EELSRkXSEcHtqHFSUgLKm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbioMH5%2FbtsQ15XXl2V%2FEELSRkXSEcHtqHFSUgLKm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;239&quot; height=&quot;66&quot; data-origin-width=&quot;239&quot; data-origin-height=&quot;66&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모양을 굳이 한 차원 더 만들어서 표시하는 데에는 앞으로 만들 인공신경망이 그렇게 원하기 때문이겠지. 그리고 확장성을 생각하더라도 출력이 1개만 있는 경우보다는 n개가 있는 경우들도 많을텐데, 그 모양을 (샘플개수, 출력개수)로 만들어 두는 것이 일반적일 것임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아, 참고로 데이터의 모양을 표시하는 방법으로 numpy에서 사용하는 튜플 형태 (M, N)이 표시하는데 더 좋아서 그걸 선호해서 쓰겠음... pytorch에서는 위에서 보는 것과 같이 shape을 출력시켜 보면, torch.Siez([ ]) 형태로 출력해준다. 그게 더 명확하긴 한데.. 여튼 설명에는 좀 귀찮다;; ㅎㅎ;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759496681252&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from torch import nn&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뉴럴네트워크 사용하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명은 Gemini의 주석달기 기능으로 달아두었다. ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759497091323&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ----------------- 1. 모델 클래스 정의 -----------------
class HeightPredictor(nn.Module):
    # 이 모델 클래스는 nn.Module의 모든 기능을 상속받습니다.
    
    # 모델의 '뼈대'와 '부품'을 준비하는 단계입니다.
    def __init__(self, input_size=2, hidden_size=64, output_size=1):
        # nn.Module의 초기화 기능을 먼저 실행합니다. (필수!)
        super().__init__()
        
        # nn.Sequential: 계층(Layer)들을 일렬로 순서대로 쌓아주는 컨테이너입니다.
        self.regressor_stack = nn.Sequential(
            # nn.Linear (선형 계층): Wx + b 연산을 수행합니다.
            # input_size=2 (나이, 몸무게)를 64개의 은닉 뉴런으로 변환합니다.
            nn.Linear(input_size, hidden_size), 
            
            # nn.ReLU (활성화 함수): 비선형성을 추가하여 모델의 표현력을 높입니다.
            # ReLu는 x가 0보다 크면 x를 그대로, 0보다 작으면 0을 출력합니다.
            nn.ReLU(),
            
            # 은닉 계층 2: 64개의 뉴런 -&amp;gt; 64개의 뉴런으로 다시 변환합니다. (깊이를 추가)
            nn.Linear(hidden_size, hidden_size),
            nn.ReLU(),
            
            # 최종 출력 계층: 64개의 뉴런 -&amp;gt; output_size=1 (키 예측값)으로 출력합니다.
            # **회귀(Regression) 문제이므로, 마지막에 활성화 함수는 사용하지 않습니다.**
            nn.Linear(hidden_size, output_size) 
        )

    # 2. 데이터의 흐름 정의 (모델을 통해 데이터가 흘러가는 계산 과정)
    def forward(self, x):
        # 입력 데이터 텐서(x)를 위에서 정의한 계층 스택에 통과시킵니다.
        predicted_height = self.regressor_stack(x)
        
        # 계산된 최종 예측값(키) 텐서를 반환합니다.
        return predicted_height&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Keras로 먼저 공부를 했던 터라, Sequential이라는 용어가 뭔가 친숙하다. 물론 방식은 다르지만, 뭐 층층히 layer와 활성함수가 추가되는 구조가 직관적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 특이? 특별?한 점은 forward 함수를 따로 만들었다는 점이다. keras에서는 모델명.predict(입력)으로 결과를 얻었는데, 여기서는 클래스명.forward(입력)으로 결과를 얻겠네. 물론 클래스를 정의하기 나름이겠지만, 일단 Gemini가 가르쳐 주는 대로 따라가 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759497733799&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ----------------- 2. 모델 사용 예시 -----------------

# 모델 인스턴스 생성: __init__ 함수가 실행됩니다.
model = HeightPredictor()

# 가짜 입력 데이터 (Batch size: 5, Features: 2)
# torch.randn(5, 2)는 (5, 2) 모양의 표준 정규 분포 난수 텐서를 만듭니다.
dummy_batch = torch.randn(5, 2) 

# forward() 실행: model(입력)을 호출하면 자동으로 forward 메서드가 실행됩니다.
predictions = model(dummy_batch)

# 결과 출력
print(&quot;### 모델 구조 (nn.Module이 자동으로 관리하는 계층): ###&quot;)
print(model)
print(&quot;-&quot; * 30)
print(f&quot;입력 데이터 모양: {dummy_batch.shape}&quot;) # (5, 2)
print(f&quot;출력 예측값 모양: {predictions.shape}&quot;) # (5, 1) - 5개 샘플의 키 예측값
print(prediction)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIWCcz/btsQ4AaLfST/W9T2zXKHsLd0mt9zBkhio1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIWCcz/btsQ4AaLfST/W9T2zXKHsLd0mt9zBkhio1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIWCcz/btsQ4AaLfST/W9T2zXKHsLd0mt9zBkhio1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIWCcz%2FbtsQ4AaLfST%2FW9T2zXKHsLd0mt9zBkhio1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;349&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;497&quot; data-origin-height=&quot;129&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGdyVv/btsQ3nb93rC/WKS6WPl3tPa49jAXQvRyr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGdyVv/btsQ3nb93rC/WKS6WPl3tPa49jAXQvRyr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGdyVv/btsQ3nb93rC/WKS6WPl3tPa49jAXQvRyr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGdyVv%2FbtsQ3nb93rC%2FWKS6WPl3tPa49jAXQvRyr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;497&quot; height=&quot;129&quot; data-origin-width=&quot;497&quot; data-origin-height=&quot;129&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델이 어떻게 생겼는지, 입력과 출력이 어떤 모양을 갖는지 볼 수 있다. 참고로 여기서 dummy_batch는 정말 아무런 값을 만들어 (실제 입력 범위랑도 다름) 넣었고, 모델도 학습되지 않은 상태이기 때문에 아무런 출력을 내뱉었다. ㅎㅎ 일단 모양만 눈여겨 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759497934514&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch.optim as optim

# 1. 모델 인스턴스 (Model Instance)
model = HeightPredictor() # Step 4에서 정의한 모델

# 2. 손실 함수 (Loss Function)
loss_fn = nn.MSELoss()

# 3. 옵티마이저 (Optimizer)
optimizer = optim.Adam(model.parameters(), lr=0.01)

print(&quot;모델 학습을 위한 준비 완료: Model, Loss Function, Optimizer 설정 완료!&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 손실함수와 옵티마이저 설정을 마쳤다. 자꾸 Keras랑 비교하게 되긴 하지만... (당분간은 어쩔 수 없다 나는 그게 더 익숙한 몸이니...) 손실함수랑 옵티마이저를 바깥에다가 따로 준비시켜 두네.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759498113868&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 필요한 모듈 및 설정 불러오기
import torch
from torch import nn
import torch.optim as optim
import matplotlib.pyplot as plt # 시각화를 위한 Matplotlib 라이브러리

# (이전 Step 4 &amp;amp; 5에서 정의했던 모델, 데이터, 손실 함수, 옵티마이저 설정은 동일)
# ... (model, X_features, y_labels, loss_fn, optimizer 정의 부분 생략)
# ----------------------------------------------------

# 학습할 에포크(Epoch) 횟수 설정
num_epochs = 1000

# 손실 값을 기록할 리스트 준비
loss_history = [] 

print(f&quot;모델 학습 시작: {num_epochs} 에포크&quot;)
for epoch in range(num_epochs):
    
    optimizer.zero_grad()
    predictions = model(X_features)
    loss = loss_fn(predictions, y_labels)
    
    # 현재 손실 값을 리스트에 기록
    loss_history.append(loss.item()) 
    
    loss.backward()
    optimizer.step()
    
    if (epoch + 1) % 100 == 0:
        print(f&quot;Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}&quot;)

print(&quot;모델 학습 완료!&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;383&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eJCOU9/btsQ30HANQD/orv1V0koIgq58D1E3En051/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eJCOU9/btsQ30HANQD/orv1V0koIgq58D1E3En051/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eJCOU9/btsQ30HANQD/orv1V0koIgq58D1E3En051/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeJCOU9%2FbtsQ30HANQD%2Forv1V0koIgq58D1E3En051%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;318&quot; data-origin-width=&quot;383&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1000번 학습 완료.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759498372983&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import matplotlib.pyplot as plt # 시각화를 위한 Matplotlib 라이브러리

# 손실 그래프 그리기 (Plotting the Loss)
plt.figure(figsize=(10, 6)) # 그래프 크기 설정
plt.plot(loss_history, label='Training Loss', color='blue') # 기록된 손실 값으로 그래프 생성
plt.title('Training Loss over Epochs') # 그래프 제목
plt.xlabel('Epoch') # X축 라벨
plt.ylabel('Mean Squared Error (MSE) Loss') # Y축 라벨
plt.grid(True) # 그리드 표시
plt.legend() # 범례 표시
plt.show() # 그래프 출력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vt3Bh/btsQ3H9iey8/dqbyvsFLxvAOJP5Ohd3Zhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vt3Bh/btsQ3H9iey8/dqbyvsFLxvAOJP5Ohd3Zhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vt3Bh/btsQ3H9iey8/dqbyvsFLxvAOJP5Ohd3Zhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvt3Bh%2FbtsQ3H9iey8%2FdqbyvsFLxvAOJP5Ohd3Zhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;536&quot; height=&quot;338&quot; data-origin-width=&quot;868&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;손실값 그래프도 출력&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 더 돌리면 더 줄어들 수도 있을 것 같은데, 1000번 더 돌려본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;315&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djKlr1/btsQ4h3q5xV/mQku9aGfkYJSoBihcxOxhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djKlr1/btsQ4h3q5xV/mQku9aGfkYJSoBihcxOxhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djKlr1/btsQ4h3q5xV/mQku9aGfkYJSoBihcxOxhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdjKlr1%2FbtsQ4h3q5xV%2FmQku9aGfkYJSoBihcxOxhk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;373&quot; height=&quot;315&quot; data-origin-width=&quot;373&quot; data-origin-height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdV2UL/btsQ1mS3aeZ/0I06KlGyMjkF20ELiBLlkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdV2UL/btsQ1mS3aeZ/0I06KlGyMjkF20ELiBLlkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdV2UL/btsQ1mS3aeZ/0I06KlGyMjkF20ELiBLlkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdV2UL%2FbtsQ1mS3aeZ%2F0I06KlGyMjkF20ELiBLlkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;327&quot; data-origin-width=&quot;841&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1000번 더?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;316&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bziHSB/btsQ2QTejpa/wDk8D91gxuv2ogKBRVaet0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bziHSB/btsQ2QTejpa/wDk8D91gxuv2ogKBRVaet0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bziHSB/btsQ2QTejpa/wDk8D91gxuv2ogKBRVaet0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbziHSB%2FbtsQ2QTejpa%2FwDk8D91gxuv2ogKBRVaet0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;378&quot; height=&quot;316&quot; data-origin-width=&quot;378&quot; data-origin-height=&quot;316&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yfPva/btsQ33ElTLu/x4t1vAkZqY3kb2rchhaPSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yfPva/btsQ33ElTLu/x4t1vAkZqY3kb2rchhaPSK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yfPva/btsQ33ElTLu/x4t1vAkZqY3kb2rchhaPSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyfPva%2FbtsQ33ElTLu%2Fx4t1vAkZqY3kb2rchhaPSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;483&quot; height=&quot;299&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 더 학습시키는 건 의미가 없어 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 모델을 이용해 예측을 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759498687301&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 새로운 가상 데이터 (나이 30세, 몸무게 80kg)로 예측
new_input = torch.tensor([[30.0, 80.0]], dtype=torch.float32)

# **평가 모드 설정 (Evaluation Mode)**
# 드롭아웃(Dropout) 등 학습 때만 필요한 기능들을 비활성화합니다.
model.eval() 

# 미분 추적 비활성화 (Disable gradient tracking)
# 예측할 때는 미분 계산이 필요 없으므로 메모리 절약 및 속도 향상을 위해 사용합니다.
with torch.no_grad():
    predicted_height = model(new_input)

# 원래 데이터 생성 공식에 따르면 예상 키는 (30 * 0.5) + (80 * 1.5) + 50 = 15 + 120 + 50 = 185cm 근처
print(f&quot;\n입력 (나이, 몸무게): (30, 80)&quot;)
print(f&quot;예측된 키: {predicted_height.item():.2f} cm&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;338&quot; data-origin-height=&quot;69&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HJyNY/btsQ4p8dW2q/znsGRlfYI9uVfDGNibIiHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HJyNY/btsQ4p8dW2q/znsGRlfYI9uVfDGNibIiHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HJyNY/btsQ4p8dW2q/znsGRlfYI9uVfDGNibIiHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHJyNY%2FbtsQ4p8dW2q%2FznsGRlfYI9uVfDGNibIiHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;338&quot; height=&quot;69&quot; data-origin-width=&quot;338&quot; data-origin-height=&quot;69&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;185cm 근처로 값이 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 질문,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 현재 모델이 일반화된 특징을 학습했다고 할 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 3000회를 학습시키면서 사실상 오버피팅을 의도했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 검증 데이터가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Gemini한테 시켜서 다음의 코드로 그래프를 찍어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759499035847&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
import matplotlib.pyplot as plt
import numpy as np

# -------------------- Step 4, 6에서 사용된 변수 가정 --------------------
# model: 학습이 완료된 HeightPredictor 모델 인스턴스
# X_features: (100, 2) 모양의 입력 데이터 텐서 (나이, 몸무게)
# y_labels: (100, 1) 모양의 실제 키 레이블 텐서
# ----------------------------------------------------------------------

# 모델을 평가 모드로 전환 (Dropout 등 비활성화)
model.eval()

# 미분 추적 비활성화 (메모리 및 속도 최적화)
with torch.no_grad():
    # 1. 모델의 예측값 얻기
    # 모든 학습 데이터 X_features에 대한 모델의 예측을 수행합니다.
    predicted_y = model(X_features)

    # 2. 텐서를 NumPy 배열로 변환 (Matplotlib 사용을 위함)
    # CPU 텐서로 변환 (.cpu()) 후, NumPy 배열로 변환 (.numpy())
    X_numpy = X_features.cpu().numpy()
    y_numpy = y_labels.cpu().numpy()
    predicted_numpy = predicted_y.cpu().numpy()

    # 3. 모델 수식 그래프를 위한 가상 데이터 생성 (몸무게만 변화)
    # 몸무게 범위를 설정하고, 나이는 평균 나이(25.0)로 고정하여 예측 라인을 그립니다.
    weight_range = np.linspace(X_numpy[:, 1].min(), X_numpy[:, 1].max(), 100).reshape(-1, 1)
    
    # 나이는 평균값(25.0)으로 고정하여 (100, 2) 모양의 입력 텐서 생성
    mean_age = torch.full((100, 1), 25.0)
    test_weights = torch.tensor(weight_range, dtype=torch.float32)
    
    # 모델 입력 텐서: (100, 2) 모양의 [고정된 평균 나이, 변화하는 몸무게]
    input_for_line = torch.cat((mean_age, test_weights), dim=1)
    
    # 모델 예측
    line_predictions = model(input_for_line)
    line_numpy = line_predictions.cpu().numpy()
    
    # 4. 실제 수식 라인 (Ground Truth Formula) 계산
    # y = (나이 * 0.5) + (몸무게 * 1.5) + 50
    true_line = (25.0 * 0.5) + (weight_range * 1.5) + 50
    
# -------------------- 5. 그래프 시각화 --------------------
plt.figure(figsize=(12, 7))

# 1. 실제 데이터 포인트 (Actual Data Points) - 산점도 (Scatter Plot)
# X_numpy[:, 1]는 몸무게(두 번째 특징)를 의미합니다.
plt.scatter(X_numpy[:, 1], y_numpy, color='skyblue', alpha=0.6, label='Actual Data Points (Weight vs. Height)')

# 2. 모델이 예측한 라인 (Learned Model Prediction Line)
# 모델이 학습한, 나이를 25세로 고정했을 때의 키 예측 라인
plt.plot(weight_range, line_numpy, color='red', linewidth=3, label='Learned Model Prediction Line (Age=25)')

# 3. 실제 데이터 생성 수식 라인 (Ground Truth Formula Line)
# 데이터가 생성된 실제 수식의 라인
plt.plot(weight_range, true_line, color='green', linestyle='--', linewidth=2, label='Ground Truth Formula (Age=25)')

plt.title('Height Prediction Visualization: Model vs. True Formula')
plt.xlabel('Weight (kg) [Input Feature]')
plt.ylabel('Height (cm) [Predicted/Actual Value]')
plt.legend()
plt.grid(True)
plt.show()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;624&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brg4Uq/btsQ12GXZrQ/1E5DsP6rscpAF3AkMtUBE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brg4Uq/btsQ12GXZrQ/1E5DsP6rscpAF3AkMtUBE0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brg4Uq/btsQ12GXZrQ/1E5DsP6rscpAF3AkMtUBE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbrg4Uq%2FbtsQ12GXZrQ%2F1E5DsP6rscpAF3AkMtUBE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1005&quot; height=&quot;624&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;624&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;25세 기준으로 몸무게와 키 그래프를 확인해보았다. 생각보다 오버피팅이 덜하다. ㅎㅎ 아마도 우리 모델 자체가 파라메터 수가 많지 않아서였거나, 데이터 자체에 랜덤 값을 충분히 넣어둬서 였을지도.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그나저나 위 시각화 코드도 한 번 공부해봐야 할텐데...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델을 평가 모드로 전환하는 부분과, with torch.no_grad():를 통해 미분 추적 비활성화를 하는 부분은 눈여겨볼만하다. 그리고 torch tensor에서 numpy 배열로 바꿔줘야 matplotlib이 사용가능한데, 이거 은근 번거롭겠네. (물론 이젠 인공지능이 다 알아서 해주겠지만..;; ㅎㅎ;;) 아, 그리고 numpy는 cpu 기반이라고 하네. 그래서 torch tensor에서 cpu()를 호출한 뒤에 바꾸네. 이것도 익숙해져야 할듯.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러고보니 앞서 모델 학습시킬 때 특별히 GPU 사용 여부를 설정하지 않았는데, GPU를 사용하고 싶다면 아래와 같이 해야 한다고 Gemini가 알려준다. (맞겠지 ㅎㅎ. 나중에 무거운 모델 학습시킬 때 확실히 비교해볼 수 있겠다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759499519882&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;device = torch.device(&quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;)
print(f&quot;사용할 장치: {device}&quot;)

model = HeightPredictor()
X_features = ... # 데이터 텐서
y_labels = ... # 레이블 텐서

model.to(device)
X_features = X_features.to(device)
y_labels = y_labels.to(device)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 GPU 장치 확인 후 모델과 입출력 데이터를 to(device)로 보내면 된다고 한다. 순전파, 손실 계산, 역전파 코드는 그대로 사용해도 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글은 MINIST 데이터와 CNN 모델로 손글씨 데이터 분류하는 예제를 만들어봐야겠다.&lt;/p&gt;</description>
      <category>Pytorch</category>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/791</guid>
      <comments>https://leestation.tistory.com/791#entry791comment</comments>
      <pubDate>Fri, 3 Oct 2025 22:53:05 +0900</pubDate>
    </item>
    <item>
      <title>PyTorch의 기본 텐서(Tensor) 이해하기. 그리고 Operation과 Autograd</title>
      <link>https://leestation.tistory.com/790</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;※ 본 글은 Google Gemini 2.5 Flash의 도움을 받아 작성될(된) 예정임을 미리 밝힙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;텐서(Tensor)&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 데이터 구조&lt;/li&gt;
&lt;li&gt;배열(Arrya)나 행렬(Matrix)를 일반화 한 것&lt;/li&gt;
&lt;li&gt;N차원 배열까지 모두 텐서라고 부름&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1759164205996&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch
import numpy as np&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Colab에서 돌릴테니 환경설정 따로 없이 torch를 import한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numpy도 익숙한 패키지이니 import&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759164299764&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data = [[1, 2], [3, 4]]
x_data = torch.tensor(data)
print(x_data)
type(x_data)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;120&quot; data-origin-height=&quot;57&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tYFjU/btsQTjWiOk4/0oVT2k1pV9w0wc9DoQEzq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tYFjU/btsQTjWiOk4/0oVT2k1pV9w0wc9DoQEzq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tYFjU/btsQTjWiOk4/0oVT2k1pV9w0wc9DoQEzq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtYFjU%2FbtsQTjWiOk4%2F0oVT2k1pV9w0wc9DoQEzq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;120&quot; height=&quot;57&quot; data-origin-width=&quot;120&quot; data-origin-height=&quot;57&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차원 list를 만든 후에 x_data라는 텐서를 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값을 출력해보니 tensor라는 거에 쌓여진 형태로 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;type은 torch.Tensor. 그냥 텐서라고 하지 말고 torch tensor라고 불러야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 그렇군.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759164593774&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;np_array = np.array(data)
x_np = torch.from_numpy(np_array)
print(x_np)
type(x_np)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 numpy array에서 torch tensor를 만들 수도 있다. 출력 결과는 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759164707964&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;x_ones = torch.ones_like(x_data) # x_data와 같은 모양이지만, 모든 값이 1인 텐서
x_rand = torch.rand_like(x_data, dtype=torch.float) # x_data와 같은 모양이지만, 무작위 값(0~1)을 가지는 Float형 텐서&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 모양의 one tensor 또는 random 값을 갖는 tensor도 만들 수 있다. 다 필요가 있는 것이겠지.. ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 텐서의 주요 속성&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;텐서를 생성한 후에는 그 텐서의 크기(모양)나 자료형을 확인하는 것이 중요&lt;/li&gt;
&lt;li&gt;모양(shape)&lt;/li&gt;
&lt;li&gt;자료형(data type)&lt;/li&gt;
&lt;li&gt;텐서가 저장된 장치(device)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1759164839036&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 텐서의 속성 확인 (Checking the tensor's attributes)
print(f&quot;텐서의 모양 (Shape): {x_data.shape}&quot;)
print(f&quot;텐서의 자료형 (Data Type): {x_data.dtype}&quot;)
print(f&quot;텐서가 저장된 장치 (Device): {x_data.device}&quot;) # 현재는 CPU일 것입니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nzi4x/btsQUbXMhlf/OWYge5fdUNpfK6oPn1VKcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nzi4x/btsQUbXMhlf/OWYge5fdUNpfK6oPn1VKcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nzi4x/btsQUbXMhlf/OWYge5fdUNpfK6oPn1VKcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNzi4x%2FbtsQUbXMhlf%2FOWYge5fdUNpfK6oPn1VKcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;277&quot; height=&quot;50&quot; data-origin-width=&quot;277&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;numpy array에서처럼 shape을 잘 보는 게 좋을텐데 shape 정보를 불러오는 형태는 같네. 자료형은 속성명을 dtype으로. 그리고 사실 device는 numpy에서는 못보던 속성이다. 이 속성은 잘 기억해둬야겠다. 그리고 출력 결과를 보니 다 torch에서 정의한 정보들(torch.Size([]), torch.int64 등)이라고 나오네.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 텐서 연산 및 &lt;span&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;GPU&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 사용&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pytorch를 사용하는 핵심 이유 중 하나가 GPU를 활용하여 연산 속도를 극대화할 수 있기 때문이라고.&lt;/li&gt;
&lt;li&gt;그래서 그런지 연산과 관련해서 그냥 흔히 쓰는 연산자 말고 torch에서 제공하는 함수를 쓰는 게 좋을 것 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1759165218507&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 텐서 생성 (Creating Tensors)
tensor = torch.tensor([[1, 2], [3, 4]])
y = torch.tensor([[5, 6], [7, 8]])

# 덧셈 (Addition)
z1 = tensor + y       # 요소별 덧셈 (Element-wise addition)
z2 = torch.add(tensor, y)

# 곱셈 (Multiplication)
z3 = tensor * y       # 요소별 곱셈 (Element-wise multiplication)
z4 = torch.mul(tensor, y)

print(f&quot;요소별 덧셈 (z1):\n{z1}&quot;)
print(z2)
print(f&quot;요소별 곱셈 (z3):\n{z3}&quot;)
print(z4)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;136&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bULYxz/btsQVtjeVTn/KakGJexannkVY5gaOcQAN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bULYxz/btsQVtjeVTn/KakGJexannkVY5gaOcQAN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bULYxz/btsQVtjeVTn/KakGJexannkVY5gaOcQAN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbULYxz%2FbtsQVtjeVTn%2FKakGJexannkVY5gaOcQAN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;136&quot; height=&quot;178&quot; data-origin-width=&quot;136&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소별 덧셈 곱셈은 ㅇㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1759165402141&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 행렬 곱셈 (Matrix Multiplication - Dot product)
# tensor는 (2, 2) 모양, y는 (2, 2) 모양입니다.
z_matmul_1 = tensor.matmul(y)
z_matmul_2 = tensor @ y # 파이썬에서 행렬 곱셈을 위한 연산자 (Operator for matrix multiplication)

print(f&quot;행렬 곱셈 결과 (z_matmul_1):\n{z_matmul_1}&quot;)
print(f&quot;행렬 곱셈 결과 (z_matmul_2):\n{z_matmul_2}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;196&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAGpN1/btsQTmr1dwx/8ZxGmUR3I5lGjsyDwiJmMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAGpN1/btsQTmr1dwx/8ZxGmUR3I5lGjsyDwiJmMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAGpN1/btsQTmr1dwx/8ZxGmUR3I5lGjsyDwiJmMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAGpN1%2FbtsQTmr1dwx%2F8ZxGmUR3I5lGjsyDwiJmMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;196&quot; height=&quot;104&quot; data-origin-width=&quot;196&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;행렬 곱셈(내적)도 ㅇㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 GPU를 써보자.&lt;/p&gt;
&lt;pre id=&quot;code_1759165512189&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# GPU (CUDA) 사용 가능 여부 확인 (Check if GPU (CUDA) is available)
if torch.cuda.is_available():
    device = torch.device(&quot;cuda&quot;)
    print(&quot;GPU (CUDA)를 사용할 수 있습니다.&quot;)
else:
    device = torch.device(&quot;cpu&quot;)
    print(&quot;GPU를 사용할 수 없으므로 CPU를 사용합니다.&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;229&quot; data-origin-height=&quot;24&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDEWCV/btsQTjWiSzx/iv8CUTbjauAk4Htg5e97wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDEWCV/btsQTjWiSzx/iv8CUTbjauAk4Htg5e97wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDEWCV/btsQTjWiSzx/iv8CUTbjauAk4Htg5e97wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDEWCV%2FbtsQTjWiSzx%2Fiv8CUTbjauAk4Htg5e97wK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;229&quot; height=&quot;24&quot; data-origin-width=&quot;229&quot; data-origin-height=&quot;24&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직은 Google Colab을 쓰고 있는데, 거기도 runtime 유형을 GPU로 선택하면 GPU를 사용가능하다고 나온다. 나중에 내 데스크톱에서도 하게 된다면 꼭 확인해볼 코드.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 torch tensor를 GPU로 이동해보자. (아까는 device 속성이 CPU였음)&lt;/p&gt;
&lt;pre id=&quot;code_1759165701652&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# CPU에 있는 텐서 (Tensor currently on CPU)
x_data = torch.tensor([[1.0, 2.0], [3.0, 4.0]]) # 주의: GPU로 옮기려면 보통 float 타입 사용

# 텐서를 설정된 장치(device)로 이동 (Move the tensor to the configured device)
x_data_on_device = x_data.to(device)

print(f&quot;원래 텐서의 장치: {x_data.device}&quot;)
print(f&quot;이동된 텐서의 장치: {x_data_on_device.device}&quot;)

# GPU 텐서에 대한 연산도 동일하게 수행됩니다. (Operations on GPU tensors are performed the same way.)
z_gpu = x_data_on_device @ x_data_on_device
print(f&quot;GPU에서 수행된 행렬 곱셈 결과:\n{z_gpu}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;264&quot; data-origin-height=&quot;90&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/U9y8F/btsQVx601zu/Fvj5YPQ7FMkAe3JHAfgxIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/U9y8F/btsQVx601zu/Fvj5YPQ7FMkAe3JHAfgxIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/U9y8F/btsQVx601zu/Fvj5YPQ7FMkAe3JHAfgxIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FU9y8F%2FbtsQVx601zu%2FFvj5YPQ7FMkAe3JHAfgxIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;264&quot; height=&quot;90&quot; data-origin-width=&quot;264&quot; data-origin-height=&quot;90&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 잘 된 거겠지. ㅎㅎ 이정도 계산으로 차이를 체감할 수는 없을 테고. 특별한 건, torch tensor를 출력할 때 device 속성이 같이 출력된다는 점. CPU로 할 때는 안 보였었는데. 여튼 이렇게 다 확인해보니 마음이 편하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그나저나 GPU를 쓰고 싶으면 이렇게 다 모든 torch tensor에 대해 device 속성을 바꿔주고 해야 하는 건가..? 함수로 퉁치는 건 안되나... 뭐 일단 그건 나중에 더 알게 되겠지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 자동 미분 및 &lt;span&gt;&lt;span&gt;&lt;span aria-hidden=&quot;true&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;Requires&amp;nbsp;Grad&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 이해하기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Keras로 인공지능 모델을 만들 때는 이런 것까지 공부하지는 않았던 것 같은데,&amp;nbsp;&lt;/li&gt;
&lt;li&gt;커스터마이징을 할 때는 필요할 수도 있다고 해서 일단 이 내용도 공부는 해본다.&lt;/li&gt;
&lt;li&gt;어차피 내용이 많아보이지는 않고, 이것도 따지고 보면 진짜 미분 계산을 내가 해야 하는 게 아니고 그냥 연산 과정을 추적하게 하는 걸 허용하겠다 말겠다 정도의 수준이니 살펴는 보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;torch tensor가 생성될 때 requires_grad=True로 설정되면 이 텐서에 대해 수행되는 모든 연산을 추적하기 시작하고, 이는 미분 값을 계산하는 데 필요한 정보를 저장함.&lt;/p&gt;
&lt;pre id=&quot;code_1759166323809&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch

# requires_grad=True로 설정된 텐서 생성
x = torch.tensor(3.0, requires_grad=True)
y = torch.tensor(4.0, requires_grad=True)

# requires_grad가 False인 텐서 (기본값)
a = torch.tensor(5.0)

print(f&quot;x의 requires_grad: {x.requires_grad}&quot;)
print(f&quot;a의 requires_grad: {a.requires_grad}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;173&quot; data-origin-height=&quot;39&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ImYBP/btsQWxSJEll/MHAKrYBsVbe0BcMzpcx7qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ImYBP/btsQWxSJEll/MHAKrYBsVbe0BcMzpcx7qK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ImYBP/btsQWxSJEll/MHAKrYBsVbe0BcMzpcx7qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FImYBP%2FbtsQWxSJEll%2FMHAKrYBsVbe0BcMzpcx7qK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;173&quot; height=&quot;39&quot; data-origin-width=&quot;173&quot; data-origin-height=&quot;39&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순전파(Forward Pass)는 입력 데이터가 모델(수식)을 통과하며 출력을 만들어내는 과정이며, 이 과정에서 Autograd가 연산 그래프르 구축한다...는데 Autograd가 뭔지는 굳이 알지 않아도 될 것 같다. requires_grad를 True로 하고 나중에 grad 속성으로 미분 값을 확인한다는 정도만 알면 될듯.&lt;/p&gt;
&lt;pre id=&quot;code_1759166493768&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 순전파: z = (x^2) + 2y
z = x**2 + 2 * y
print(f&quot;순전파 결과 z: {z}&quot;)

# 미분 값 계산: z를 x와 y로 미분
# z.backward()를 호출하면 z가 정의된 연산 그래프를 역순으로 거슬러 올라가며 미분값을 계산합니다.
# 주의: 스칼라 값(하나의 숫자)에 대해서만 .backward()를 직접 호출할 수 있습니다.
z.backward()

# 계산된 미분 값 (Gradient) 확인
# 미분값은 텐서의 .grad 속성에 저장됩니다.

# 1. z를 x로 미분: dz/dx = 2x
# x=3.0 이므로, dz/dx = 2 * 3.0 = 6.0
print(f&quot;x의 미분 값 (dz/dx): {x.grad}&quot;)

# 2. z를 y로 미분: dz/dy = 2
# y=4.0 이더라도, dz/dy = 2
print(f&quot;y의 미분 값 (dz/dy): {y.grad}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;175&quot; data-origin-height=&quot;55&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sAhEd/btsQTPHHtDC/t4rXFWVsKRp3SKjayhetzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sAhEd/btsQTPHHtDC/t4rXFWVsKRp3SKjayhetzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sAhEd/btsQTPHHtDC/t4rXFWVsKRp3SKjayhetzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsAhEd%2FbtsQTPHHtDC%2Ft4rXFWVsKRp3SKjayhetzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;175&quot; height=&quot;55&quot; data-origin-width=&quot;175&quot; data-origin-height=&quot;55&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 수식에서 해당 x 값에서의 편미분값, 해당 y 값에서의 편미분값을 얻을 수 있다. 신긔신긔&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Pytorch</category>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/790</guid>
      <comments>https://leestation.tistory.com/790#entry790comment</comments>
      <pubDate>Tue, 30 Sep 2025 02:25:01 +0900</pubDate>
    </item>
    <item>
      <title>PyTorch 공부 시작...</title>
      <link>https://leestation.tistory.com/789</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Tensorflow 기반의 Keras로 인공지능 공부를 꽤 했었는데, 어느새 보니 이제는 PyTorch가 대세가 되어 버렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyTorch가 그렇게 어렵지 않다고 하던데... 그리고 Keras로 인공지능 공부를 해보았었으니... 이 악물고(까지는 아니기를...) 다시 공부를 시작해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Google Colab에서 일단 기본 내용을 익히고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 내 데스크탑에서 환경설정하고 구동시켜볼 예정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굿럭.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;a href=&quot;https://leestation.tistory.com/790&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PyTorch의&amp;nbsp;기본&amp;nbsp;텐서(Tensor)&amp;nbsp;이해하기.&amp;nbsp;그리고&amp;nbsp;Operation과&amp;nbsp;Autograd&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;a href=&quot;https://leestation.tistory.com/791&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PyTorch로&amp;nbsp;기본&amp;nbsp;인공신경망&amp;nbsp;만들기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. &lt;a href=&quot;https://leestation.tistory.com/795&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PyTorch로 CNN 만들기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Pytorch</category>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/789</guid>
      <comments>https://leestation.tistory.com/789#entry789comment</comments>
      <pubDate>Tue, 30 Sep 2025 01:37:02 +0900</pubDate>
    </item>
    <item>
      <title>공학자를 위한 Bioengineering 입문 (시작하며)</title>
      <link>https://leestation.tistory.com/788</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;본인 소개를 잠깐 하자면 필자는 외우는 걸 싫어해서 바이오와 담 쌓고 살아온 사람이다. 그리고 공학과 수학은 수식만 이해하면 부가적인 설명을 다 안 읽어도 이해가 되는 부분이었기에 (물론 이제 와서 드는 생각이지만 이게 좋은 공부 법은 아니었는듯;;) 더욱이 공학 쪽에 전념해서 살아왔었는데... 나이 40이 된 이제 제2의 질풍노도의 시기를 겪으면서, 공학만 공부해서는 더 큰 가치를 창출하는데에 다소 한계가 있을 것 같아 (변명일 수도 있지만..) 새로운 분야에 도전을 생각하게 되었다. 안그래도 인공지능 시대가 다가오면서 공학만 공부해서는 인공지능에게 그 자리를 많이 내어주게 될 것 같고, 또 거꾸로 인공지능 덕을 본다면 훨씬 더 다양한 분야를 공부할 수 있겠다 싶었기에, 새로운 분야에 대한 도전을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 그래서 왜 다른 여러 분야가 있지만 Bio 쪽이냐 할 수도 있겠지만, 그 부분은 나를 오프라인에서 만나는 분들에게 해 줄 이야기로 남겨두고... 어쨌든 Bioengineering을 내 인생 제2막의 공부 분야로 삼게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이 글을 시작으로 정말 초심자의 마음으로 Bio 관련 공부를 시작하려 한다. 어쩌면 초반은 정말 쌩 생명공항 기초가 될 수도 있겠다. 그래도 나름 방향을 잡은 것이 있다면, 세포와 유전자 분석을 하는 Bioengineering 쪽이 되겠다. 그냥 Bioengineering 입문이라고 할 수도 있겠지만, 이미 공학자로 내 용어나 생각이 절어져 있는 만큼, 아무래도 비교 대상이 공학이 될 것 같고, Bioengineering에도 engineering이라는 용어가 들어있으니 뭐 괜찮을지도. 그리고 어쩌면 인공지능 시대에 나와 같은 고민으로 Bio 공부를 시작하는 공학자들이 더러 있을지 모르니 이 글의 제목을 &quot;공학자를 위한 Bioengineering 입문&quot;이라 적어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글이 첫 글인 만큼 앞으로 쓰게 될 글들의 목록은 이 글에 계속 추가적으로 정리해나갈 예정이다. 아직 어떤 순서로 공부를 해야 할지 모르기 때문에 공부하면서 글의 번호나 순서를 바꿀 수도 있으니 일단 이 글을 초창기에 읽게되는 분들이라면 아래 목록은 아무 것도 모르는 채로 적은 것이라는 것을 이해해주기 바란다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 시작하며 (본 글)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.1 &lt;a href=&quot;https://leestation.tistory.com/792&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Bioengineering이란? Bio와 Engineering&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.2 &lt;a href=&quot;https://leestation.tistory.com/794&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;세포 및 신경 전달&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 정보 및 작동 부품&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.1 유전자 정보: DNA, 염색체, 유전&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2.2 단백질 및 효소&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 분석 도구 및 방법론&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.1 유전자 분석 기술&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.1.1 시퀀싱과 NGS&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.1.2 PCR&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.2 데이터 처리 및 해석&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.2.1 바이오인포메틱스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.2.2 통계적 접근 및 시각화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 응용 사례&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.1 생검&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4.2 질병 모델링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;99. 용어정리&lt;/p&gt;</description>
      <category>공학자를위한 Bioengineering 입문</category>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/788</guid>
      <comments>https://leestation.tistory.com/788#entry788comment</comments>
      <pubDate>Tue, 30 Sep 2025 01:31:37 +0900</pubDate>
    </item>
    <item>
      <title>The Future of Content Creation: Can the Invisible Hand Guide Us Through the Age of AI?</title>
      <link>https://leestation.tistory.com/787</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;The world of content creation has taken a significant leap forward with the rise of artificial intelligence (AI) services that can easily create images, videos, and characters, as well as AI chatbots like ChatGPT. It's now much easier to create content, new services, and apps using AI, and this has both positive and negative implications.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;On the one hand, we can expect the emergence of many advanced new services that will transform how we interact with technology. On the other hand, we are likely to see a proliferation of low-level and fake services that could mislead and deceive users.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;With the development of video production technology, anyone can now create videos, and while there are many high-quality videos on YouTube, there are also many low-quality and fake videos. This trend raises questions about which generation is more susceptible to being swayed and afflicted with low-level/fake services and information.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It's possible that young people who grew up with AI and the internet will be better equipped to distinguish service quality and identify fake news. However, it's also possible that they will be more vulnerable to manipulation by sophisticated AI algorithms that can personalize content and tailor it to their preferences.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;At the same time, we should not discount the role of the &quot;invisible hand&quot; in the information market. While there have been calls for strong centralized information control in the era of turbid information, major news outlets are still struggling to survive, and individual broadcasters who gained popularity quickly can easily fade away. The things that survive are those that contain clear 'value' in them.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As we enter the era of user-created content, feedback, and peer reviews will become more important than ever. We need to be more active in providing feedback and evaluating the quality of content to ensure that only the best content rises to the top.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In conclusion, the rise of AI services has both positive and negative implications for content creation. While it has made it easier to create new services and apps, it has also created a fertile ground for low-quality and fake services. We need to be vigilant in evaluating the quality of content and providing feedback to ensure that only the best content survives in the information market.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 글은 내가 한글로 원문을 적고, ChatGPT에게 다시 좀 다듬어달라고 한 내용이다. 아래는 원문&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChatGPT&amp;nbsp;뿐&amp;nbsp;아니라,&amp;nbsp;이미지,&amp;nbsp;영상,&amp;nbsp;캐릭터&amp;nbsp;등을&amp;nbsp;쉽게&amp;nbsp;만들어주는&amp;nbsp;인공지능&amp;nbsp;서비스가&amp;nbsp;많아지면서,&amp;nbsp;이를&amp;nbsp;이용해서&amp;nbsp;컨텐츠와&amp;nbsp;신종&amp;nbsp;서비스,&amp;nbsp;앱(app)들을&amp;nbsp;만드는&amp;nbsp;것이&amp;nbsp;굉장히&amp;nbsp;쉬워진&amp;nbsp;시대가&amp;nbsp;성큼&amp;nbsp;다가왔다.&amp;nbsp;아니,&amp;nbsp;사실&amp;nbsp;이미&amp;nbsp;쉬웠는데&amp;nbsp;이제는&amp;nbsp;훨씬훨씬훨씬&amp;nbsp;쉬워졌다.&amp;nbsp;그리고&amp;nbsp;그게&amp;nbsp;성큼&amp;nbsp;정도가&amp;nbsp;아니라&amp;nbsp;크게&amp;nbsp;빵(Big&amp;nbsp;Bang)&amp;nbsp;하고&amp;nbsp;다가왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이에&amp;nbsp;따라&amp;nbsp;고도화된&amp;nbsp;신종&amp;nbsp;서비스가&amp;nbsp;많이&amp;nbsp;출현할&amp;nbsp;것인데,&amp;nbsp;마찬가지로&amp;nbsp;저급/가짜&amp;nbsp;서비스도&amp;nbsp;많이&amp;nbsp;출현할&amp;nbsp;것&amp;nbsp;같다.&amp;nbsp;영상제작기술의&amp;nbsp;발달로&amp;nbsp;누구나&amp;nbsp;영상을&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;시대가&amp;nbsp;오면서,&amp;nbsp;YouTube에&amp;nbsp;고급&amp;nbsp;영상도&amp;nbsp;많아졌지만,&amp;nbsp;저급/가짜&amp;nbsp;영상도&amp;nbsp;많아졌듯이. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한&amp;nbsp;편으로는&amp;nbsp;그러한&amp;nbsp;것이&amp;nbsp;당연한&amp;nbsp;시대에&amp;nbsp;교육을&amp;nbsp;받기&amp;nbsp;시작한&amp;nbsp;젊은&amp;nbsp;층들은&amp;nbsp;서비스&amp;nbsp;퀄리티를&amp;nbsp;구분하는&amp;nbsp;능력이&amp;nbsp;기성세대보다&amp;nbsp;더&amp;nbsp;빠르고&amp;nbsp;정확하게&amp;nbsp;할지도&amp;nbsp;모르겠다는&amp;nbsp;생각이&amp;nbsp;든다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저급/가짜&amp;nbsp;서비스와&amp;nbsp;정보에&amp;nbsp;휘둘리고&amp;nbsp;시달리게&amp;nbsp;되는&amp;nbsp;세대는&amp;nbsp;어느&amp;nbsp;세대일까.&amp;nbsp;가짜&amp;nbsp;뉴스를&amp;nbsp;톡방에&amp;nbsp;공유하며&amp;nbsp;진짜인&amp;nbsp;것&amp;nbsp;처럼&amp;nbsp;이야기하는&amp;nbsp;모습이&amp;nbsp;어디에서&amp;nbsp;더&amp;nbsp;많이&amp;nbsp;보이는지&amp;nbsp;생각해보라. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한,&amp;nbsp;고급&amp;nbsp;서비스를&amp;nbsp;찾아내는&amp;nbsp;데&amp;nbsp;더&amp;nbsp;밝은&amp;nbsp;눈을&amp;nbsp;가지고&amp;nbsp;있는&amp;nbsp;세대는&amp;nbsp;젊은&amp;nbsp;세대일까,&amp;nbsp;기성세대일까?&amp;nbsp;ㅎㅎ&amp;nbsp;(나라면,&amp;nbsp;전자에&amp;nbsp;배팅하겠다.) &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UCC(User&amp;nbsp;Created&amp;nbsp;Contents)&amp;nbsp;시대가&amp;nbsp;오면서,&amp;nbsp;혼탁한&amp;nbsp;정보의&amp;nbsp;시대&amp;nbsp;속에서&amp;nbsp;강력한&amp;nbsp;중앙&amp;nbsp;집권적&amp;nbsp;정보&amp;nbsp;통제가&amp;nbsp;필요하다는&amp;nbsp;주장도&amp;nbsp;있었다.&amp;nbsp;그리고&amp;nbsp;여전히&amp;nbsp;그러한&amp;nbsp;역할이&amp;nbsp;필요한&amp;nbsp;지점들도&amp;nbsp;종종&amp;nbsp;보일&amp;nbsp;때가&amp;nbsp;있다.&amp;nbsp; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나&amp;nbsp;&quot;보이지&amp;nbsp;않는&amp;nbsp;손&quot;이&amp;nbsp;&quot;정보&amp;nbsp;시장&quot;에도&amp;nbsp;동작하지&amp;nbsp;않을까&amp;nbsp;하고&amp;nbsp;나는&amp;nbsp;생각해본다.&amp;nbsp;메이저&amp;nbsp;언론사들도&amp;nbsp;살아남고자&amp;nbsp;여전히&amp;nbsp;발버둥치고&amp;nbsp;있고,&amp;nbsp;한순간에&amp;nbsp;인기를&amp;nbsp;얻었던&amp;nbsp;개인&amp;nbsp;방송인도&amp;nbsp;어느&amp;nbsp;순간에는&amp;nbsp;쉽게&amp;nbsp;사그라든다.&amp;nbsp;도태되지&amp;nbsp;않는&amp;nbsp;것들에는&amp;nbsp;그&amp;nbsp;안에&amp;nbsp;분명한&amp;nbsp;'가치'를&amp;nbsp;담고&amp;nbsp;있는&amp;nbsp;것들이었다.&amp;nbsp;(물론,&amp;nbsp;뇌피셜임...) &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가&amp;nbsp;쓰는&amp;nbsp;이&amp;nbsp;글&amp;nbsp;또한&amp;nbsp;사회&amp;nbsp;구성원들과&amp;nbsp;사회&amp;nbsp;현상의&amp;nbsp;관찰&amp;nbsp;등을&amp;nbsp;통해&amp;nbsp;피드백&amp;nbsp;될&amp;nbsp;것이다.&amp;nbsp;피드백과&amp;nbsp;동료평가(peer-review)도&amp;nbsp;더&amp;nbsp;활발해지겠지.&amp;nbsp;아니,&amp;nbsp;더&amp;nbsp;활발해져야&amp;nbsp;한다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(ChatGPT로&amp;nbsp;쓴&amp;nbsp;글&amp;nbsp;아님.&amp;nbsp;믿거나&amp;nbsp;말거나.)&lt;/p&gt;</description>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/787</guid>
      <comments>https://leestation.tistory.com/787#entry787comment</comments>
      <pubDate>Sun, 26 Feb 2023 20:51:26 +0900</pubDate>
    </item>
    <item>
      <title>Installing ROS on WSL2 Ubuntu 20.04LTS on Windows 11</title>
      <link>https://leestation.tistory.com/786</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Assuming that WSL2 Ubuntu 20.04LTS is installed.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;If you are using WSL2, you can display Ubuntu GUI windows on the host Windows os.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Refer to the [Existing WSL Install] section.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps#existing-wsl-install&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps#existing-wsl-install&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Install ROS Neotic&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;http://wiki.ros.org/noetic/Installation/Ubuntu&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://wiki.ros.org/noetic/Installation/Ubuntu&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Connect WSL2 &amp;lt;-&amp;gt; Windows using VcXSrv&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;a href=&quot;https://learn.microsoft.com/en-us/windows/wsl/networking&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://learn.microsoft.com/en-us/windows/wsl/networking&lt;/a&gt;&lt;/p&gt;</description>
      <category>Etc./연구관련</category>
      <author>이원형</author>
      <guid isPermaLink="true">https://leestation.tistory.com/786</guid>
      <comments>https://leestation.tistory.com/786#entry786comment</comments>
      <pubDate>Sun, 30 Oct 2022 20:37:32 +0900</pubDate>
    </item>
  </channel>
</rss>