1. 생물학적 뉴런
- 생물학전 뉴런은 신경계를 구성하는 기본 단위로, 정보를 수집, 처리, 전달하는 기능을 담당합니다.
- 뉴런은 크게 3가지 주요 부분으로 나뉩니다.
- 수상돌기
- 다른 뉴런이나 외부 자극으로부터 신호를 받아들이는 역할
- 세포체
- 수상돌기에서 받은 신호를 처리하고 통합
- 축삭
- 세포체에서 처리된 신호는 축삭을 통해 다름 뉴런이나 근육 혹은 샘과 같은 다른 조직으로 전달
- 축삭 끝에는 시냅스가 있어 화학적 신호 또는 전기적 신호를 통해 다른 세포와 연결됩니다.
- 뉴런은 이러한 작용을 통해 신경계가 복잡한 정보를 효율적으로 처리하고 전달할 수 있도록 돕습니다.

2. 인공신경망(ANN)
- 인공신경망(Artificial Neural Network, ANN)은 생물학적 신경망의 구조와 기능을 모방한 계산 모델로, 여러 개의 인공 뉴런을 계층적으로 연결하여 구성됩니다.
- 기본적으로 입력층(input layer), 은닉층(hidden layer), 출력층(output layer)으로 이루어지며, 각 층의 뉴런들은 다음 층의 뉴런과 연결되어 데이터를 전달하고 처리합니다.
- 입력 데이터는 가중치와 활성화 함수 등의 연산을 거쳐 점차 복잡한 특징을 추출하며, 최종적으로 출력층에서 예측값이나 결과를 생성합니다.
- 신경망은 데이터에 기반한 학습을 통해 가중치를 조정하면서 문제를 해결하도록 최적화됩니다.
- 이러한 특성 덕분에 이미지 분류, 음성 인식, 자연어 처리 등 다양한 분야에서 뛰어난 성능을 발휘하며, 딥러닝의 주요 구성 요소로 활용됩니다.
- ANN은 다층 구조와 비선형 활성화 함수를 통해 복잡한 데이터 간의 관계를 학습할 수 있는 강력한 도구입니다.
3. 인공 신경망의 역사
3-1. 1940년대 ~ 1980년대
- 인공 신경망(Artificial Neural Network, ANN)의 역사는 1940년대에 시작된 초기 개념부터 현대의 딥러닝에 이르기까지 긴 발전 과정을 거쳤습니다. 1943년, 워런 맥컬록(Warren McCulloch)과 월터 피츠(Walter Pitts)는 생물학적 뉴런을 수학적으로 모델링한 맥컬록-피츠 뉴런을 제안하며 ANN의 기초를 마련했습니다. 1958년, 프랭크 로젠블렛(Frank Rosenblatt)은 단층 퍼셉트론(perceptron)을 개발하여 ANN이 학습과 분류 문제를 해결할 수 있음을 보여주었습니다. 그러나 1969년, 마빈 민스키(Marvin Minsky)와 시모어 페이퍼트(Seymour Papert)는 퍼셉트론의 한계를 지적하며, 비선형 문제를 해결하지 못한다는 사실을 제시하였고, 이는 ANN 연구의 침체기로 이어졌습니다.
※ 퍼셉트론
- 퍼셉트론(Perceptron)은 1958년 프랭크 로젠블렛이 제안한 인공 뉴런 모델로, 가장 간단한 형태의 인공 신경망입니다.
- input값과 weight값의 곱을 모두 더한 값에 bias를 더하고, 이를 활성화 함수로 변환하여 binary ouput을 생성합니다.
- 학습과정에서는 output값이 실제와 다를 경우, weight를 조정하는 방식으로 학습이 이루어집니다.
- 퍼셉트론은 단층 구조로 선형적으로 분리 가능한 문제를 해결할 수 있지만, XOR 문제와 같은 비 선형적으로 분리되는 문제를 해결하지 못하는 한계가 있습니다.

논리 회귀(단층 퍼셉트론)로 AND 문제 풀기
import torch
import torch.nn as nn
import torch.optim as optim
X = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]])
y = torch.FloatTensor([[0], [0], [0], [1]])
model = nn.Sequential(
nn.Linear(2, 1),
nn.Sigmoid()
)
optimizer = optim.SGD(model.parameters(), lr=1)
epochs = 1000
for epoch in range(epochs + 1) :
y_pred = model(X)
loss = nn.BCELoss()(y_pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 100 == 0 :
y_bool = (y_pred >= 0.5).float()
accuracy = (y == y_bool).float().sum() / len(y) * 100
print(f'Epoch {epoch:4d}/{epochs}, Loss: {loss:.6f}, Accuracy: {accuracy:.2f}%')
논리 회귀(단층 퍼셉트론)로 OR 문제 풀기
X = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]])
y = torch.FloatTensor([[0], [1], [1], [1]])
model = nn.Sequential(
nn.Linear(2, 1),
nn.Sigmoid()
)
optimizer = optim.SGD(model.parameters(), lr=1)
epochs = 1000
for epoch in range(epochs + 1) :
y_pred = model(X)
loss = nn.BCELoss()(y_pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 100 == 0 :
y_bool = (y_pred >= 0.5).float()
accuracy = (y == y_bool).float().sum() / len(y) * 100
print(f'Epoch {epoch:4d}/{epochs}, Loss: {loss:.6f}, Accuracy: {accuracy:.2f}%')
논리 회귀(단층 퍼셉트론)로 XOR 문제 풀기
X = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]])
y = torch.FloatTensor([[0], [1], [1], [0]])
model = nn.Sequential(
nn.Linear(2, 1),
nn.Sigmoid()
)
optimizer = optim.SGD(model.parameters(), lr=1)
epochs = 1000
for epoch in range(epochs + 1) :
y_pred = model(X)
loss = nn.BCELoss()(y_pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 100 == 0 :
y_bool = (y_pred >= 0.5).float()
accuracy = (y == y_bool).float().sum() / len(y) * 100
print(f'Epoch {epoch:4d}/{epochs}, Loss: {loss:.6f}, Accuracy: {accuracy:.2f}%')
3-2. 1980년대 ~ 2000년대
- 1980년대에 이르러 역전파(Backpropagation) 알고리즘이 재발견 되면서 ANN이 다시 주목받기 시작했습니다.
- 특히, 제프리 힌턴 등 연구자들의 공헌으로 다층 퍼셉트론(Multi-Layer Perceptron, MLP)이 비선형 문제를 해결할 수 있음을 보였고, ANN 연구가 활발해졌습니다.
- 1990년대에는 합성곱 신경망(CNN)과 순환 신경망(RNN) 등 특화된 구조가 개발되며 ANN이 더 복잡한 문제를 다룰 수 있게 되었습니다.

논리 회귀(다층 퍼셉트론)로 XOR 문제 풀기
model = nn.Sequential(
nn.Linear(2, 4),
# 여기서 Sigmoid는 곡선화 시키기 위해 사용함(활성화 함수)
nn.Sigmoid(),
nn.Linear(4, 1),
nn.Sigmoid()
)
print(model)

X = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]])
y = torch.FloatTensor([[0], [1], [1], [0]])
optimizer = optim.SGD(model.parameters(), lr=1)
epochs = 1000
for epoch in range(epochs + 1) :
y_pred = model(X)
loss = nn.BCELoss()(y_pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 100 == 0 :
y_bool = (y_pred >= 0.5).float()
accuracy = (y == y_bool).float().sum() / len(y) * 100
print(f'Epoch {epoch:4d}/{epochs}, Loss: {loss:.6f}, Accuracy: {accuracy:.2f}%')
4. 비선형 활성화 함수
- 비선형 활성화 함수는 신경망에서 선형 변환만으로는 표현할 수 없는 복잡한 비선형 패턴을 학습할 수 있도록 도와주는 함수입니다.
- input값을 비 선형적으로 변환하여 신경망의 layer마다 다른 특징을 학습하게 하며, 이를 통해 선형 함수들의 조합으로는 표현할 수 없는 복잡한 함수와 데이터 분포를 모델링 할 수 있습니다.
- 주로 ReLU, Sigmoid, Tanh 등이 있으며, 이 함수들은 출력값을 제한하거나 왜곡하여 학습을 안정화하고, 신경망이 더 강력한 표현력을 가지도록 만듭니다.
4-1. Sigmoid
- Sigmoid Function은 비선형 활성화 함수 중 하나로, 주로 신경망에서 출력값을 0과 1사이로 스케일링 하는 데에 사용됩니다.
- 입력값 x가 양수일수록 출력값은 1에 가까워지고, 음수일수록 0에 가까워집니다.
- Sigmoid는 입력 값을 확률처럼 해석할 수 있도록 변환하는 특징 때문에 Binary Classification 문제에서 자주 사용되지만, 출력의 미분값이 작은 구간에서 Gradient 소실 문제가 발생할 수 있어, 최근에는 ReLU와 같은 활성화 함수들이 더 많이 사용됩니다.
- 하지만, Sigmoid는 모델의 특정 부분이나 확률적 해석이 필요한 경우에 유용하게 활용됩니다.
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x) :
return 1 / (1 + np.exp(-x))
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.plot([0, 0], [1.0, 0.0], ':')
plt.title('Sigmoid Function')
plt.show()
4-2. Tanh(하이퍼볼릭탄젠트)
- 하이퍼볼릭 탄젠트(Tanh)는 비선형 활성화 함수 중 하나로, 입력 값을 -1에서 1 사이의 값으로 변환하는 함수입니다.
- 입력 값이 큰 양수일수록 1에 가까워지고, 큰 음수일수록 -1에 가까워집니다.
- Tanh는 출력 범위가 대칭적이어서 Sigmoid와 달리 출력이 0을 기준으로 분포되므로, 데이터가 양수와 음수로 분리되는 문제에서 더 적합합니다.
- 그러나 입력 값이 매우 크거나 작을 경우 기울기가 0에 가까워지는 '기울기 소실' 문제가 발생 할 수 있습니다.
- Tanh는 주로 출력 값의 분포를 중심에 맞추고 싶을 때 사용됩니다.
x = np.arange(-5.0, 5.0, 0.1)
y = np.tanh(x)
plt.plot(x, y)
plt.plot([0, 0], [1.0, 0.0], ':')
plt.axhline(y=0, color='orange', linestyle='--')
plt.title('Tanh Function')
plt.show()
4-3. ReLU(렐루)
- ReLU(Rectified Linear Unit)는 신경망에서 가장 널리 사용되는 비선형 함수 중 하나로, 입력 값이 0보다 크면 그대로 출력하고, 0 이하이면 0을 출력하는 함수 입니다.
- 출력값이 양수인 경우 기울기가 1로 일정하게 유지되므로 '기울기 소실' 문제를 완화 할 수 있습니다.
- 이로 인해 학습이 빠르고 효율적이며, 깊은 신경망에서 성능이 뛰어난 것으로 알려져 있습니다.
- 그러나 ReLU는 입력값이 0 이하일 때 기울기가 0이 되어 학습되지 않는 '죽은 ReLU' 문제가 발생 할 수 있으며, 이를 보완하기 위해 Leaky ReLU와 같은 변형 함수들이 개발되었습니다.
def relu(x) :
return np.maximum(0, x)
x = np.arange(-5.0, 5.0, 0.1)
y = relu(x)
plt.plot(x, y)
plt.plot([0, 0], [5.0, 0.0], ':')
plt.title('ReLU Function')
plt.show()
※ ReLU 그래프 자체가 직선인데 활성화 함수로 사용하는 이유

(주로 Output Layer에서 다중 클래스 분류를 하기 위해 사용되는 Softmax함수도 비선형성을 갖기 때문에 같이 넣어봤습니다.)
4-4. Softmax 시각화
- Softmax Function은 비선형 활성화 함수로, 주로 Multi Classification 문제에서 출력층에 사용됩니다.
- 입력값의 지수 값을 계산하고, 이를 모든 출력값의 지수 값의 합으로 나누어 각 클래스에 속할 확률을 반환합니다.
- 출력값이 0과 1사이의 확률로 정규화 되며, 모든 클래스의 확률 합이 1이됩니다.
- Softmax는 모델이 각 클래스에 속할 가능성을 명확히 보여주기 때문에, Classification 문제에서 최종 결정을 내리는 데에 유용합니다.
x = np.arange(-5.0, 5.0, 0.1)
y = np.exp(x) / np.sum(np.exp(x))
plt.plot(x, y)
plt.title('Softmax Function')
plt.show()
※ Softmax가 비선형성 함수인 이유
