단 60줄의 코드로 GPT를 만들었다고?

60줄의 코드로 GPT를 만든 친구가 있네요.  깃헙 저장소에 코드가 있는데요, 너무 짧아 콜랩에서도 돌아가게 해 봤습니다.
여러분이 질문도 넣어보고 파라미터 값도 변경해 가면서 테스트해 보세요. 또 코드가 짧으니 GPT학습에도 좋습니다.

콜랩에 실행되는 코드의 설명입니다.

이 코드는 GPT-2 모델을 실행하는 함수와 그와 관련된 여러 유틸리티 함수들로 구성되어 있습니다. 주요 함수와 기능은 다음과 같습니다:

  1. gelu(x): GELU (Gaussian Error Linear Unit) 활성화 함수를 구현한 함수입니다.
  2. softmax(x): 소프트맥스 함수를 구현한 함수입니다. 입력 배열의 각 요소를 소프트맥스 함수에 적용하여 확률 분포로 변환합니다.
  3. layer_norm(x, g, b, eps): 레이어 정규화를 수행하는 함수입니다. 입력 배열을 정규화하고, 스케일과 오프셋을 적용합니다.
  4. linear(x, w, b): 선형 변환을 수행하는 함수입니다. 입력 배열과 가중치 행렬, 편향 벡터를 곱하여 출력을 계산합니다.
  5. ffn(x, c_fc, c_proj): 피드포워드 신경망(feed-forward network)을 구현한 함수입니다. 입력 배열을 선형 변환하고, GELU 활성화 함수를 적용한 후 다시 선형 변환합니다.
  6. attention(q, k, v, mask): 어텐션(attention) 메커니즘을 구현한 함수입니다. 주어진 쿼리(query), 키(key), 값(value) 배열을 사용하여 어텐션 가중치를 계산하고, 가중합을 구합니다.
  7. mha(x, c_attn, c_proj, n_head): 멀티 헤드 어텐션(multi-head attention)을 수행하는 함수입니다. 입력 배열을 선형 변환한 후, 각각의 어텐션 헤드로 분리합니다. 어텐션 가중치를 계산하고 헤드를 다시 병합한 후, 선형 변환을 적용합니다.
  8. transformer_block(x, mlp, attn, ln_1, ln_2, n_head): 트랜스포머 블록(transformer block)을 구현한 함수입니다. 멀티 헤드 어텐션과 피드포워드 신경망을 포함하며, 입력 배열에 이러한 계층을 적용하여 출력을 계산합니다.
  9. gpt2(inputs, wte, wpe, blocks, ln_f, n_head): GPT-2 모델을 실행하는 함수입니다. 토큰과 위치 임베딩을 결합한 후, 여러 개의 트랜스포머 블록을 순차적으로 통과시켜 출력을 계산합니다.
  10. `generate(inputs, params, n_head, n_tokens_to_generate)` 함수는 입력 시퀀스(inputs), 모델 파라미터(params), 헤드 개수(n_head), 생성할 토큰 개수(n_tokens_to_generate)를 인자로 받아서 텍스트를 생성하는 함수입니다.함수 내부에서는 다음과 같은 작업이 수행됩니다:

    1. `tqdm` 라이브러리를 사용하여 “generating” 메시지와 함께 진행 상황을 표시합니다.

    2. 지정된 토큰 개수(n_tokens_to_generate)만큼 반복하는 루프를 실행합니다. 이 루프는 자동 회귀적인 디코딩을 수행합니다.

    3. `gpt2(inputs, **params, n_head=n_head)`를 호출하여 모델의 순방향 전파(forward pass)를 수행하고 로짓(logits)을 얻습니다.

    4. 로짓 중 가장 큰 값을 가진 인덱스를 선택하여 다음 토큰을 결정합니다. 이는 탐욕적인(greedy) 샘플링 방식입니다.

    5. 예측된 다음 토큰을 입력 시퀀스(inputs)에 추가합니다.

    6. 생성된 텍스트를 반환하기 위해 입력 시퀀스(inputs)에서 마지막에 위치한 n_tokens_to_generate 개수만큼의 토큰을 잘라냅니다.

    따라서 `generate` 함수를 호출하면 입력 시퀀스(inputs)와 모델 파라미터(params)를 사용하여 지정된 개수의 토큰을 생성하고, 해당 토큰들을 반환합니다. 이러한 토큰들은 후속 처리를 통해 텍스트로 디코딩될 수 있습니다.

아래는 코드입니다.

 

 

#로칼 컴퓨터에서 할거 아니니까, picoGPT/gpt2.py 의 내용 그대로 실행되게 붙여 넣음
import numpy as np


def gelu(x):
    return 0.5 * x * (1 + np.tanh(np.sqrt(2 / np.pi) * (x + 0.044715 * x**3)))


def softmax(x):
    exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
    return exp_x / np.sum(exp_x, axis=-1, keepdims=True)


def layer_norm(x, g, b, eps: float = 1e-5):
    mean = np.mean(x, axis=-1, keepdims=True)
    variance = np.var(x, axis=-1, keepdims=True)
    x = (x - mean) / np.sqrt(variance + eps)  # normalize x to have mean=0 and var=1 over last axis
    return g * x + b  # scale and offset with gamma/beta params


def linear(x, w, b):  # [m, in], [in, out], [out] -> [m, out]
    return x @ w + b


def ffn(x, c_fc, c_proj):  # [n_seq, n_embd] -> [n_seq, n_embd]
    # project up
    a = gelu(linear(x, **c_fc))  # [n_seq, n_embd] -> [n_seq, 4*n_embd]

    # project back down
    x = linear(a, **c_proj)  # [n_seq, 4*n_embd] -> [n_seq, n_embd]

    return x


def attention(q, k, v, mask):  # [n_q, d_k], [n_k, d_k], [n_k, d_v], [n_q, n_k] -> [n_q, d_v]
    return softmax(q @ k.T / np.sqrt(q.shape[-1]) + mask) @ v


def mha(x, c_attn, c_proj, n_head):  # [n_seq, n_embd] -> [n_seq, n_embd]
    # qkv projection
    x = linear(x, **c_attn)  # [n_seq, n_embd] -> [n_seq, 3*n_embd]

    # split into qkv
    qkv = np.split(x, 3, axis=-1)  # [n_seq, 3*n_embd] -> [3, n_seq, n_embd]

    # split into heads
    qkv_heads = list(map(lambda x: np.split(x, n_head, axis=-1), qkv))  # [3, n_seq, n_embd] -> [3, n_head, n_seq, n_embd/n_head]

    # causal mask to hide future inputs from being attended to
    causal_mask = (1 - np.tri(x.shape[0], dtype=x.dtype)) * -1e10  # [n_seq, n_seq]

    # perform attention over each head
    out_heads = [attention(q, k, v, causal_mask) for q, k, v in zip(*qkv_heads)]  # [3, n_head, n_seq, n_embd/n_head] -> [n_head, n_seq, n_embd/n_head]

    # merge heads
    x = np.hstack(out_heads)  # [n_head, n_seq, n_embd/n_head] -> [n_seq, n_embd]

    # out projection
    x = linear(x, **c_proj)  # [n_seq, n_embd] -> [n_seq, n_embd]

    return x


def transformer_block(x, mlp, attn, ln_1, ln_2, n_head):  # [n_seq, n_embd] -> [n_seq, n_embd]
    # multi-head causal self attention
    x = x + mha(layer_norm(x, **ln_1), **attn, n_head=n_head)  # [n_seq, n_embd] -> [n_seq, n_embd]

    # position-wise feed forward network
    x = x + ffn(layer_norm(x, **ln_2), **mlp)  # [n_seq, n_embd] -> [n_seq, n_embd]

    return x


def gpt2(inputs, wte, wpe, blocks, ln_f, n_head):  # [n_seq] -> [n_seq, n_vocab]
    # token + positional embeddings
    x = wte[inputs] + wpe[range(len(inputs))]  # [n_seq] -> [n_seq, n_embd]

    # forward pass through n_layer transformer blocks
    for block in blocks:
        x = transformer_block(x, **block, n_head=n_head)  # [n_seq, n_embd] -> [n_seq, n_embd]

    # projection to vocab
    x = layer_norm(x, **ln_f)  # [n_seq, n_embd] -> [n_seq, n_embd]
    return x @ wte.T  # [n_seq, n_embd] -> [n_seq, n_vocab]


def generate(inputs, params, n_head, n_tokens_to_generate):
    from tqdm import tqdm

    for _ in tqdm(range(n_tokens_to_generate), "generating"):  # auto-regressive decode loop
        logits = gpt2(inputs, **params, n_head=n_head)  # model forward pass
        next_id = np.argmax(logits[-1])  # greedy sampling
        inputs.append(int(next_id))  # append prediction to input

    return inputs[len(inputs) - n_tokens_to_generate :]  # only return generated ids


def main(prompt: str, n_tokens_to_generate: int = 40, model_size: str = "124M", models_dir: str = "models"):
    from utils import load_encoder_hparams_and_params

    # load encoder, hparams, and params from the released open-ai gpt-2 files
    encoder, hparams, params = load_encoder_hparams_and_params(model_size, models_dir)

    # encode the input string using the BPE tokenizer
    input_ids = encoder.encode(prompt)

    # make sure we are not surpassing the max sequence length of our model
    assert len(input_ids) + n_tokens_to_generate < hparams["n_ctx"]

    # generate output ids
    output_ids = generate(input_ids, params, hparams["n_head"], n_tokens_to_generate)

    # decode the ids back into a string
    output_text = encoder.decode(output_ids)

    return output_text

인공지능이용 보안기술 – 전자상거례에서 사기성 주문 여부 판단

머신러닝은 사기성 주문 판별에 유용한 도구 중 하나입니다. 일반적으로, 사기성 주문 판별을 위한 머신러닝 모델을 만들기 위해서는 다음과 같은 단계를 거칩니다.

  1. 데이터 수집: 머신러닝 모델을 학습시키기 위해서는 레이블링된 사기성 주문 데이터와 정상 주문 데이터가 필요합니다.
  2. 데이터 전처리: 수집된 데이터를 전처리하여 모델 학습에 적합한 형식으로 변환합니다. 이 단계에서는 데이터의 결측치, 이상치 등을 처리하고, 필요한 특성을 추출하거나 생성합니다.
  3. 모델 학습: 전처리된 데이터를 사용하여 머신러닝 모델을 학습시킵니다. 이 단계에서는 주로 분류 알고리즘을 사용하여 주문이 사기인지 아닌지를 판별하는 이진 분류 모델을 만듭니다.
  4. 모델 평가 및 성능 향상: 학습된 모델을 평가하고 성능을 향상시키기 위해 모델의 하이퍼파라미터를 조정하거나 다른 분류 알고리즘을 시도할 수 있습니다. 또한, 새로운 데이터에 대한 모델의 예측 성능을 평가하기 위해 검증 데이터를 사용할 수 있습니다.
  5. 모델 배포: 학습된 모델을 실제 운영 환경에 배포하여 사용합니다. 이 단계에서는 모델을 서버나 클라우드에 배포하거나, 모델 API를 만들어 다른 애플리케이션에서 사용할 수 있도록 합니다.

이러한 과정을 거쳐 만들어진 머신러닝 모델은 주문 데이터의 특징을 분석하여 사기성 주문 여부를 판단할 수 있습니다. 주문의 금액, 결제 방식, 주문자 정보, 배송지 정보 등 다양한 특성을 고려하여 모델이 예측한 결과를 바탕으로 사기성 주문을 차단하거나 확인하는 등의 대응 방안을 수립할 수 있습니다.

패턴 분석 기술을 사용하여 사기성 주문 여부를 판단하는 예시 코드는 다음과 같습니다.

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

# 데이터셋을 로드합니다.
dataset = pd.read_csv('주문데이터.csv')

# 입력 데이터와 출력 데이터를 분리합니다.
X = dataset.drop('fraud', axis=1)
y = dataset['fraud']

# 학습 데이터와 테스트 데이터를 분리합니다.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 모델을 정의하고 학습합니다.
model = DecisionTreeClassifier()
model.fit(X_train, y_train)

# 테스트 데이터로 예측을 수행합니다.
y_pred = model.predict(X_test)

# 모델의 정확도를 측정합니다.
accuracy = accuracy_score(y_test, y_pred)
print('모델의 정확도: {:.2f}%'.format(accuracy * 100))

이 예시 코드에서는 사기성 주문 여부를 판단하는 모델을 학습하고, 테스트 데이터로 예측을 수행하여 모델의 정확도를 측정합니다. 이때, 입력 데이터로는 주문 내역의 금액, 결제 방법, IP 주소 등의 정보가 사용될 수 있습니다. 이러한 정보를 바탕으로 모델은 주문이 사기인지 아닌지를 판단하게 됩니다.

인공지능이용 보안기술 – 스팸 필터링

인공지능은 보안 분야에서도 다양하게 활용됩니다. 스팸 필터링은 이메일이나 메시지 등에서 스팸으로 분류되는 내용을 차단하는 기술입니다. 인공지능을 활용하면, 머신러닝 알고리즘을 이용해 스팸 메시지의 패턴을 학습하고, 새로운 스팸 메시지를 탐지하여 차단할 수 있습니다.

예들 들어 브라우저의 JavaScript 코드가 사용자의 행동 패턴과 이전 상호작용에 대한 정보를 수집하여, – 예를 들어 클릭, 키보드 입력, 마우스 이동 등- 이런 패턴을 기반으로 사용자가 로봇인지 아닌지를 판단합니다.
기술의 간단한 예시를 보겠습니다.

실행하기

 

# 데이터셋을 정의합니다.
dataset = [
  {'input': [0.1, 0.2, 0.3], 'output': [1]},
  {'input': [0.4, 0.5, 0.6], 'output': [1]},
  {'input': [0.7, 0.8, 0.9], 'output': [0]},
  {'input': [0.2, 0.3, 0.4], 'output': [1]},
  {'input': [0.5, 0.6, 0.7], 'output': [0]}
]

위 코드에서 입력 데이터의 패턴은 ‘input’ 키에 대응되는 값입니다. 이 예시에서는 각 데이터가 3차원 벡터로 이루어져 있으며, 이 벡터는 사용자의 클릭, 키보드 입력, 마우스 이동 등의 동작을 수치화한 값일 수 있습니다. 모델은 이러한 입력 데이터의 패턴을 학습하여, 로봇이 아닌 사용자와의 차이점을 찾아내고 이를 바탕으로 로봇 여부를 판단합니다. 따라서 입력 데이터의 패턴이 모델의 정확도와 판단력에 큰 영향을 미치게 됩니다. 이를 실제 웹사이트에서 사용하려면, 이벤트 리스너를 추가하여 사용자의 클릭, 키보드 입력, 마우스 이동 등의 동작을 감지하고 이를 모델에 입력 데이터로 전달하면 됩니다.

예를 들어 아래는 마우스 클릭 이벤트 리스너를 추가하는 예시입니다.

# 예시: 마우스 클릭 이벤트 리스너 추가
from browser import document

def on_mouse_click(event):
    # 클릭 위치를 수치화하여 input 값으로 사용
    x = event.clientX
    y = event.clientY
    input_data = [x, y]

    # 모델에 입력 데이터를 전달하여 결과를 예측
    result = model.predict(input_data)

    # 결과에 따른 동작 수행
    if result > 0.5:
        # 로봇일 가능성이 높음
        print("Suspicious activity detected!")
    else:
        # 로봇이 아닐 가능성이 높음
        print("Human user")
        
document.bind('click', on_mouse_click)

이외에도, 키보드 입력, 마우스 이동 등의 이벤트를 감지하는 방법은 다양하므로, 상황에 맞게 적절한 이벤트 리스너를 추가하여 사용자의 동작을 입력 데이터로 변환하여 모델에 전달할 수 있습니다.

전체 코드는 다음과 같습니다.

import tensorflow as tf
import numpy as np

# 데이터셋을 정의합니다.
dataset = [
  {'input': [0.1, 0.2, 0.3], 'output': [1]},
  {'input': [0.4, 0.5, 0.6], 'output': [1]},
  {'input': [0.7, 0.8, 0.9], 'output': [0]},
  {'input': [0.2, 0.3, 0.4], 'output': [1]},
  {'input': [0.5, 0.6, 0.7], 'output': [0]}
]

# 모델을 정의합니다.
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Dense(units=4, input_shape=[3], activation='relu'))
model.add(tf.keras.layers.Dense(units=1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam')

# 모델을 학습합니다.
xs = np.array([data['input'] for data in dataset])
ys = np.array([data['output'] for data in dataset])
history = model.fit(xs, ys, epochs=100)

# 예측합니다.
input = np.array([0.3, 0.4, 0.5])
output = model.predict(np.array([input]))
print(output[0][0])

 

 

[cafe AI] 머신러닝을 이용한 이미지 인식

  • 이 강의는 shop2world 의 캐나다 몬트리올에서 진행되는 일반인 대상의 AI 강의인 cafe AI 2023 강의 시리즈 교재입니다.
  • 강의 참여를 원하시는 분은 info@shop2world.com 으로 신청 주세요.
    강의 언어는 영어, 불어, 한국어 입니다.
  • 강의 결과 실행: [cafe AI] 머신러닝을 이용한 이미지 인식기법

머신러닝을 이용하여 이미지를 인식하는 예시로는, 손글씨 숫자 인식 프로그램이 있습니다. 이 프로그램은 숫자가 쓰인 이미지를 입력으로 받아 머신러닝 알고리즘을 통해 숫자를 인식합니다.

머신러닝 알고리즘은 먼저 숫자 이미지 데이터셋을 사용해 학습합니다. 학습을 통해 이미지에서 특징을 추출하고, 이를 바탕으로 숫자를 인식할 수 있는 모델을 만들어냅니다. 이렇게 만들어진 모델은 새로운 이미지를 입력으로 받아, 학습한 내용을 바탕으로 숫자를 인식할 수 있습니다.

이러한 손글씨 숫자 인식 프로그램은 손글씨 숫자를 자동으로 인식해주는데 사용될 수 있습니다. 이를 응용하여 우편물에서 우편번호를 인식하는 등 다양한 분야에서 활용될 수 있습니다.

먼저 실행을 해 보겠습니다.
실행하기

이 예제에서는 MNIST 데이터셋을 사용합니다. MNIST는 0부터 9까지의 숫자 이미지 데이터셋으로, 이미지 분류 문제에서 가장 대표적인 데이터셋 중 하나입니다.
Keras가 제공하는 데이터셋을 이용합니다.

먼저, 필요한 라이브러리와 데이터를 불러옵니다.

import tensorflow as tf
from tensorflow import keras
import numpy as np
mnist = keras.datasets.mnist
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

이제 데이터셋을 살펴보겠습니다.

print(train_images.shape)   # (60000, 28, 28)
print(len(train_labels))    # 60000
print(test_images.shape)    # (10000, 28, 28)
print(len(test_labels))     # 10000

총 60,000개의 학습 데이터와 10,000개의 테스트 데이터가 있습니다. 각 이미지는 28×28 크기의 흑백 이미지입니다.

데이터 전처리를 수행합니다. 픽셀값을 0과 1 사이로 정규화하고, 레이블을 범주형으로 변환합니다.

train_images = train_images / 255.0
test_images = test_images / 255.0
train_labels = keras.utils.to_categorical(train_labels)
test_labels = keras.utils.to_categorical(test_labels)

이제 모델을 구성합니다. 2개의 은닉층과 1개의 출력층으로 구성된 인공신경망을 사용합니다.

model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation=tf.nn.relu),
    keras.layers.Dense(64, activation=tf.nn.relu),
    keras.layers.Dense(10, activation=tf.nn.softmax)
])

이제 모델을 컴파일합니다.

model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

모델을 학습시킵니다.

model.fit(train_images, train_labels, epochs=5)

마지막으로 모델을 평가합니다.

test_loss, test_acc = model.evaluate(test_images, test_labels)
print('Test accuracy:', test_acc)

전체 코드는 다음과 같습니다.

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.datasets import mnist

# MNIST 데이터셋 로드
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 데이터 전처리
x_train = x_train / 255.0
x_test = x_test / 255.0

# 모델 구성
model = tf.keras.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dense(10, activation='softmax')
])

# 모델 컴파일
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# 모델 훈련
model.fit(x_train, y_train, epochs=5, validation_data=(x_test, y_test))

# 모델 평가
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=2)
print('\nTest accuracy:', test_acc)

# 예측 결과 확인
predictions = model.predict(x_test)

# 예측 결과 시각화
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  plt.imshow(x_test[i], cmap='gray')
  plt.axis('off')
  predicted_label = np.argmax(predictions[i])
  true_label = y_test[i]
  if predicted_label == true_label:
    color = 'blue'
  else:
    color = 'red'
  plt.subplot(num_rows, 2*num_cols, 2*i+2)
  plt.bar(range(10), predictions[i], color=color)
  plt.xticks(range(10))
  plt.ylim([0, 1])
  plt.tight_layout()

이 코드는 TensorFlow를 사용하여 MNIST 데이터셋에 있는 손글씨 숫자 이미지를 분류하는 모델을 학습하고, 학습된 모델을 이용하여 테스트 데이터셋에 있는 이미지를 분류하는 코드입니다. 코드 실행을 위해서는 TensorFlow와 NumPy, Matplotlib 라이브러리가 필요합니다. 또한, Google Colab에서 실행할 경우 !pip install tensorflow numpy matplotlib 명령어를 사용하여 라이브러리를 설치해야 합니다.

매개변수 (parameter)

“매개변수”란 머신 러닝 모델의 내부적인 상태값을 나타내는 말입니다. 모델의 학습 과정에서 최적의 매개변수 값을 찾아내는 것이 모델 학습의 목표 중 하나입니다.

매개변수는 일상생활에서 남녀 관계에서 자주 사용되는 ‘조건’에 비유할 수 있습니다.

예를 들어, 데이트를 하러 갈 때 여자친구가 “나한테 좋은 말 좀 해줘”라고 요구한다면, 이때의 ‘좋은 말’은 매개변수 역할을 합니다. 여자친구는 조건으로 ‘좋은 말’을 지정하고, 이에 따라 남자친구는 이를 만족시키기 위해 노력합니다. 이때, 남자친구가 여러 가지 방법으로 ‘좋은 말’을 전달하면서 여자친구의 반응을 관찰하며 조금씩 조정하고 개선해 나가는 것은 머신러닝에서 말하는 ‘학습’ 과정과 유사합니다.

머신러닝에서도 모델에 입력되는 다양한 변수들 중에서 중요한 역할을 하는 변수들이 있습니다. 이러한 변수들을 매개변수라고 부르며, 모델이 데이터를 학습하면서 이 매개변수들의 값을 조정하면서 예측 결과를 개선해 나가게 됩니다.

머신 러닝 모델은 입력 데이터에 대해 예측값을 출력하는데, 이때 모델은 일련의 연산 과정을 통해 입력을 처리하고 출력을 생성합니다. 이때 모델의 매개변수는 이러한 연산 과정에서 사용되는 가중치(weight)와 편향(bias) 등의 값들을 의미합니다. 모델 학습 과정에서는 이러한 매개변수 값을 최적화하여 입력에 대한 정확한 예측을 할 수 있도록 합니다.

“ChatGPT-3″의 경우, 1750억개의 매개변수를 가지는데, 이는 모델이 처리할 수 있는 언어의 다양성과 정확도를 높이기 위해 매우 많은 수의 매개변수를 사용하는 것입니다. 이렇게 많은 매개변수를 가지는 모델은 매우 복잡한 함수를 근사(approximate)할 수 있으므로, 자연어 처리 분야에서 높은 성능을 보이고 있습니다.

매개변수의 개수가 1750억개라는 것은, ChatGPT-3 모델 내부에 사용되는 가중치(weight)와 편향(bias) 값 등을 모두 합쳐서 1750억개가 있다는 것입니다. 즉, 이 매개변수들이 모델 내부에서 각각 어떻게 설정되느냐에 따라 모델이 입력된 데이터를 처리하고 출력하는 방식이 결정됩니다.

따라서, 이 매개변수들을 조정하는 것은 모델의 학습 과정에서 이루어지며, 이를 위해서는 대량의 데이터와 연산능력이 필요합니다. 그러므로, 모델을 학습시키기 위해서는 대규모 컴퓨팅 자원과 많은 시간이 필요합니다.

매개변수  10개의 경우

예를 들어, 매개변수를 다음과 같이 정의해보겠습니다.

  1. 외모 – 0은 싫어하고 1은 좋아합니다.
  2. 부모님 – 0은 싫어하고 1은 좋아합니다.
  3. 자신감 – 0은 낮고, 1은 높습니다.
  4. 돈 – 0은 많이 없으면 싫어하고, 1은 많으면 좋아합니다.
  5. 관심사 – 0은 맞지 않으면 싫어하고, 1은 맞으면 좋아합니다.
  6. 유머감각 – 0은 없으면 싫어하고, 1은 있으면 좋아합니다.
  7. 자신감 – 0은 없으면 싫어하고, 1은 있으면 좋아합니다.
  8. 성격 – 0은 싫어하고, 1은 좋아합니다.
  9. 연봉 – 0은 적으면 싫어하고, 1은 많으면 좋아합니다.
  10. 교육 – 0은 낮으면 싫어하고, 1은 높으면 좋아합니다.

위의 매개변수를 사용하여, 다음과 같은 코드를 작성할 수 있습니다.

실행하기

import random
# 매개변수 초기화
params = [random.randint(0, 1) for _ in range(10)]

# 여자가 "사랑해" 라고 출력하는 함수
def say_love(params):
score = 0
score += 2 * params[0] - 1
score += 2 * params[1] - 1
score += 2 * params[2] - 1
score += 2 * params[3] - 1
score += 2 * params[4] - 1
score += 2 * params[5] - 1
score += 2 * params[6] - 1
score += 2 * params[7] - 1
score += 2 * params[8] - 1
score += 2 * params[9] - 1
if score > 0:
return "사랑해"
else:
return "싫어해"

print(say_love(params))

위 코드는 10개의 매개변수를 초기화한 후, say_love 함수를 사용하여 남자의 조건을 입력하면 여자가 “사랑해” 또는 “싫어해”를 출력합니다. 각 매개변수는 0 또는 1의 값을 가지며, 남자의 조건을 평가하는데 사용됩니다. 함수 내부에서는 매개변수의 값들을 합산하여 score 값을 계산하고, score 값이 양수인 경우 “사랑해”, 음수인 경우 “싫어해”를 출력합니다.

위의 예시 프로그램에서 가중치(weight)는 남자가 여자에게 제시하는 조건들로, 편향(bias)은 조건들이 여자에게 얼마나 긍정적인 영향을 끼치는지를 조절하는 값입니다. 가중치와 편향은 모델의 학습을 통해 최적화되며, 최적화된 가중치와 편향이 사용됩니다. 즉, 이 프로그램에서는 가중치와 편향을 사람이 수동으로 조절하는 것이 아니라, 머신 러닝 알고리즘이 학습을 통해 최적의 값을 찾아내는 것입니다.

위의 프로그램에서 예를 들어 score += 2 * params[5] – 1 을 통해 가중치와 편향을 보겠습니다.params[5]는 가중치(weight)에 해당합니다. 가중치는 모델이 학습을 통해 자동으로 조정하는 값으로, 입력 데이터와 곱해져서 중요한 특성을 강조하거나 약화시키는 역할을 합니다.

2는 가중치에 곱해지는 계수(coefficient)로, 이 값이 높을수록 해당 가중치가 결과값에 미치는 영향이 더 커집니다.

-1은 편향(bias)에 해당합니다. 편향은 모델이 학습을 통해 자동으로 조정하는 값으로, 입력 데이터에 더해져서 결과값에 일정한 영향을 끼치는 역할을 합니다. 이 예시에서는 -1을 더해주므로, 편향이 음의 영향을 끼치게 됩니다.

 

Cost 함수 크로스 앤트로피 (Cross -Entropy)

크로스 엔트로피(Cross-Entropy)는 머신러닝에서 분류 문제를 해결하기 위해 주로 사용되는 손실 함수입니다. 이를 예를 들어 남녀 관계로 설명해보겠습니다.

예를 들어, 남녀 구분 문제에서는 머신러닝 모델이 주어진 입력 데이터(성별, 나이, 키 등)를 바탕으로 해당 데이터가 남자일 확률과 여자일 확률을 예측합니다. 이 때, 크로스 엔트로피는 모델이 예측한 확률 분포와 실제 레이블 분포 간의 차이를 계산하는 손실 함수입니다.

만약, 모델이 특정 입력 데이터를 바탕으로 해당 데이터가 남자일 확률을 0.7, 여자일 확률을 0.3으로 예측했다고 가정해봅시다. 그리고 실제로 해당 데이터가 남자인 경우를 1로, 여자인 경우를 0으로 나타낸다면, 실제 레이블 분포는 1과 0이 됩니다.

이 경우, 크로스 엔트로피 손실 함수는 다음과 같이 계산됩니다.

import numpy as np

# 모델이 예측한 확률 분포
y_pred = np.array([0.7, 0.3])

# 실제 레이블 분포
y_true = np.array([1, 0])

# 크로스 엔트로피 손실 함수 계산
loss = -np.sum(y_true * np.log(y_pred))
print(loss) # 0.35667494393873245

실행하기

따라서, 위의 경우 모델이 예측한 확률 분포와 실제 레이블 분포 간의 차이를 나타내는 크로스 엔트로피 손실 함수의 값은 약 0.357이 됩니다. 이 값은 모델의 예측이 실제와 얼마나 차이가 나는지를 나타내며, 이 값을 최소화하는 것이 모델을 학습시키는 목표가 됩니다.

 

 

One Hot Encoding

활률값으로 나온 벡터를 0,1로 표현해 주는 원핫 엔코딩에 대한 설명입니다.

확률값으로 나온 벡터를 0과 1로 표현하는 원핫 인코딩은 범주형 데이터를 다룰 때 사용하는 인코딩 방법 중 하나입니다. 각 범주(category)마다 새로운 이진 벡터를 만들어 해당 범주에 해당하는 인덱스는 1로, 그 외에는 모두 0으로 만들어주는 방식입니다.

예를 들어, 색깔을 범주형 변수로 가정하고, “빨강”, “녹색”, “파랑” 세 가지의 색깔이 있다고 합시다. 이를 원핫 인코딩을 사용하여 0과 1로 표현하면 아래와 같습니다.

색깔 빨강 녹색 파랑
빨강 1 0 0
녹색 0 1 0
파랑 0 0 1

위와 같이 범주의 개수만큼의 차원을 가진 이진 벡터를 만들어 각 범주에 해당하는 인덱스는 1로, 그 외에는 모두 0으로 만들어줍니다.

corlab에서 실행 가능한 코드는 다음과 같습니다.

#One Hot Encoding

import numpy as np

# 범주형 변수의 값
colors = ['빨강', '녹색', '파랑', '파랑', '빨강', '초록']

# 중복 제거한 범주 목록
categories = list(set(colors))

# 범주 수 만큼의 차원을 가진 이진 벡터 생성
one_hot = np.zeros((len(colors), len(categories)))

# 해당 범주의 인덱스에만 1 할당
for i, color in enumerate(colors):
    j = categories.index(color)
    one_hot[i, j] = 1

print(one_hot)

위 코드를 실행하면 다음과 같은 출력 결과를 얻을 수 있습니다.

[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]
 [0. 0. 1.]
 [1. 0. 0.]
 [0. 1. 0.]]

위 출력 결과에서 각 행이 원핫 인코딩된 범주를 나타냅니다. 첫 번째 행은 “빨강”을 나타내며, 이에 해당하는 첫 번째 열만 1이고 나머지는 모두 0입니다.

아직 이해가 안되시나요?

다시 쉽게 설명해 보겠습니다.

One Hot Encoding은 범주형 데이터를 수치형 데이터로 변환하는 방법 중 하나입니다. 이 방법은 각 범주마다 하나의 이진 변수를 만들어 해당 범주에 속하면 1, 속하지 않으면 0으로 표시하는 방식입니다.

예를 들어, 성별을 나타내는 범주형 데이터가 있다고 가정해보겠습니다. 이 데이터는 “남성”과 “여성” 두 가지 범주로 구성되어 있습니다. One Hot Encoding을 적용하면 “남성” 범주에 해당하는 변수와 “여성” 범주에 해당하는 변수를 각각 만들어 해당하는 범주에는 1, 해당하지 않는 범주에는 0으로 표시합니다.

이러한 방식은 범주형 데이터를 수치형 데이터로 변환하기 때문에 다양한 머신러닝 알고리즘에서 적용할 수 있습니다. 또한 이진 변수로 표현되기 때문에 데이터의 차원이 매우 커질 수 있습니다. 그러나 이러한 차원의 증가는 일반적으로 희소 행렬(sparse matrix) 문제를 야기할 수 있습니다. 따라서 One Hot Encoding을 적용할 때는 주의해야 합니다.

예를 들어, “남성”과 “여성” 외에도 다양한 범주가 있는 경우, 모든 범주에 대한 이진 변수를 만들어 차원을 크게 늘리는 것은 비효율적일 수 있습니다. 이 경우에는 다른 인코딩 방법을 고려해야 합니다.

 

머신러닝의 꽃 RNN (순환 신경망)

RNN은 Recurrent Neural Network의 약자입니다. 이는 입력 데이터의 시퀀스를 처리하는 데 적합한 인공 신경망 구조를 나타내며, 이전 스텝의 출력값이 다음 스텝의 입력값으로 이어지는 구조를 가지고 있습니다.

음악 작곡은 시계열 데이터를 다루는 문제이기 때문에 RNN이 자연스럽게 적합합니다. 또 RNN은 자연어 처리 분야에서 많이 사용되며, 예를 들어 문장 내 단어의 시퀀스를 입력으로 받아 문장의 감정을 판단하는 것과 같은 작업에 활용됩니다. 예를 들어 “I love you”와 “I hate you”는 전체 문장은 동일하지만 단어의 순서에 따라 전혀 다른 의미를 가지고 있습니다. RNN은 이러한 문맥 정보를 학습하여 이전 시퀀스의 정보를 현재 시퀀스에 반영함으로써 감정 분류 작업과 같은 문제를 처리할 수 있습니다.

다른 예시로는 주가 예측과 같은 시계열 데이터 분석 문제에서 RNN을 활용할 수 있습니다. 주가 데이터는 이전 주가 데이터에 영향을 받아 현재 주가가 결정되기 때문에 RNN을 사용하면 이전 시점의 주가 데이터를 반영하여 더욱 정확한 예측을 할 수 있습니다.

따라서, RNN은 머신러닝에서 시계열 데이터를 다루는 데 있어서 핵심적인 역할을 수행하며, 이를 바탕으로 다양한 분야에서 활용되고 있기 때문에 “머신러닝의 꽃”이라 불리게 된 것입니다.

RNN(Recurrent Neural Network)은 딥러닝의 일종으로, 인공신경망 모델 중 하나입니다. 따라서 RNN은 머신러닝과 딥러닝 모두에 해당합니다. 다만, RNN은 딥러닝의 한 종류인 심층 순환 신경망(Deep Recurrent Neural Network)의 일부분으로 더 넓은 범주에 속하게 됩니다.

이처럼 RNN은 순서가 있는 데이터를 다루는데 강점을 가지는 딥러닝 모델 중 하나입니다. 예를 들어, 텍스트 데이터나 시계열 데이터 등 순차적으로 입력되는 데이터를 처리할 때 자주 사용됩니다.

개와 원숭이의 관계를 예로 들어보겠습니다. 우리는 개와 원숭이를 쉽게 구분할 수 있습니다. 하지만 컴퓨터에게 개와 원숭이를 구분하라고 하면 어떻게 할까요? 개와 원숭이를 구분하는 데 필요한 정보를 컴퓨터에게 어떻게 전달할 수 있을까요?

이때 RNN을 사용할 수 있습니다. RNN은 과거의 입력 데이터를 기억하고, 현재 입력 데이터와 함께 처리합니다. 예를 들어, 개와 원숭이를 구분하는 문제에서 RNN은 과거에 입력된 데이터(예를 들어, 눈의 색, 귀의 크기 등)를 기억하고, 현재 입력된 데이터(예를 들어, 다리의 길이, 꼬리의 길이 등)와 함께 처리합니다. 이렇게 RNN은 과거의 정보를 활용하여 현재 입력 데이터를 처리하므로, 개와 원숭이를 구분하는 데 좀 더 효과적인 결과를 얻을 수 있습니다.

아래는 RNN을 이용한 텍스트 생성 예시입니다. 이 예시는 CorlLab에서 실행할 수 있습니다. 예시 코드에서는 RNN 모델을 학습하여 새로운 텍스트를 생성합니다.
이 코드는 니체의 글을 읽어들이고, 그것을 기반으로 RNN 모델을 학습시켜 니체 스타일의 글을 생성하는 것입니다. 학습된 모델을 사용하여 새로운 글을 생성할 수 있습니다. 이 모델은 이전에 학습된 텍스트의 특징을 파악하고 그 특징을 기반으로 새로운 글을 생성하는 방식으로 작동합니다.

물론 생성된 글이 니체 스타일인지 여부를 정확히 평가하는 것은 어렵습니다. 하지만 생성된 글이 니체 스타일을 잘 따르고 있는지의 정도를 평가할 수는 있습니다.

만약 이 모델이 잘 학습되었다면, 생성된 글은 니체의 특징을 따를 것입니다. 예를 들어, 니체의 글은 많은 경우 길고 복잡한 문장 구조를 가지고 있으며, 생각의 흐름이 길게 이어지는 경우가 많습니다. 또한, 니체는 독창적인 어휘와 문장 구조를 사용하여 그의 작품을 특별하게 만들었습니다. 따라서, 생성된 글에서 이러한 니체의 특징이 나타난다면, 그 글이 니체 스타일의 글일 가능성이 높습니다.

예를 들어, 다음과 같은 문장이 생성된다면 니체 스타일의 글로 간주될 수 있습니다: “세상은 끊임없이 새로워지고 우리의 삶도 변화를 거듭합니다.” 이 문장은 길고 복잡한 문장 구조를 가지고 있으며, 새로운 개념을 소개하면서 생각의 흐름이 연결되어 있습니다. 또한, “세상”, “끊임없이”, “새로워지고”, “우리의 삶도”, “변화를 거듭합니다”와 같은 니체 특유의 어휘와 문장 구조가 사용되었습니다.

# 필요한 패키지 import
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.layers import Dense, LSTM
import numpy as np

# 데이터셋 로드
path = keras.utils.get_file(
    'nietzsche.txt',
    origin='https://s3.amazonaws.com/text-datasets/nietzsche.txt')

with open(path, encoding='utf-8') as f:
    text = f.read().lower()

# 문자열을 숫자로 변환
chars = sorted(list(set(text)))
char_indices = dict((c, i) for i, c in enumerate(chars))
indices_char = dict((i, c) for i, c in enumerate(chars))

# 입력 시퀀스와 타깃 시퀀스 생성
maxlen = 40
step = 3
sentences = []
next_chars = []
for i in range(0, len(text) - maxlen, step):
    sentences.append(text[i:i + maxlen])
    next_chars.append(text[i + maxlen])
x = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.bool)
y = np.zeros((len(sentences), len(chars)), dtype=np.bool)
for i, sentence in enumerate(sentences):
    for t, char in enumerate(sentence):
        x[i, t, char_indices[char]] = 1
    y[i, char_indices[next_chars[i]]] = 1

# 모델 구성
model = keras.models.Sequential()
model.add(LSTM(128, input_shape=(maxlen, len(chars))))
model.add(Dense(len(chars), activation='softmax'))

# 모델 컴파일
optimizer = keras.optimizers.RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer)

# 모델 학습
model.fit(x, y, batch_size=128, epochs=20)

# 모델 사용
def sample(preds, temperature=1.0):
    preds = np.asarray(preds).astype('float64')
    preds = np.log(preds) / temperature
    exp_preds = np.exp(preds)
    preds = exp_preds / np.sum(exp_preds)
    probas = np.random.multinomial(1, preds, 1)
    return np.argmax(probas)

start_index = np.random.randint(0, len(text) - maxlen - 1)
for temperature in [0.2, 0.5, 1.0, 1.2]:
    generated_text = text[start_index: start_index + maxlen]
    print('--- temperature:', temperature)
    sys.stdout.write(generated_text)

    for i in range(400):
        x_pred = np.zeros((1, maxlen, len(chars)))
        for t, char in enumerate(generated_text):
            x_pred[0, t, char_indices[char]] = 1.

        preds = model.predict(x_pred, verbose=0)[0]
        next_index = sample(preds, temperature)
        next_char = indices_char[next_index]

        generated_text += next_char
        generated_text = generated_text[1:]

        sys.stdout.write(next_char)
        sys.stdout.flush()
    print()

위의 코드는 RNN 모델을 사용하여 텍스트 생성을 수행합니다. 먼저, 텍스트 데이터셋을 로드하고, 문자열을 숫자로 변환합니다. 그 다음, 입력 시퀀스와 타깃 시퀀스를 생성합니다.

이 모델은 LSTM 레이어를 사용하여 시퀀스 데이터를 학습합니다. LSTM 레이어는 이전 상태를 기억하고 새로운 입력에 대한 출력을 생성하는 방법으로 시퀀스 데이터를 모델링합니다.

모델은 컴파일되고 학습됩니다. 이 모델은 categorical crossentropy 손실함수와 RMSprop 옵티마이저를 사용하여 컴파일합니다. 그 다음, 학습 데이터를 사용하여 모델을 학습합니다.

마지막으로, 모델은 텍스트를 생성하는 데 사용됩니다. 시작 인덱스를 무작위로 선택하고, 다양한 온도를 사용하여 샘플링된 텍스트를 출력합니다. 이를 통해 모델이 생성한 텍스트가 온도에 따라 어떻게 달라지는지 확인할 수 있습니다.

여기서 말하는 “온도(temperature)”는 날씨의 온도와는 다른 개념입니다. 이전에 말씀드린 것처럼, RNN 모델이 다음 문자를 예측할 때 확률 분포를 사용합니다. 이때, softmax 함수를 사용하면 각 문자에 대한 확률 분포를 구할 수 있습니다. 이 확률 분포를 사용해 다음 문자를 샘플링하게 되는데, 이때 온도(temperature)가 사용됩니다.

온도는 샘플링할 때 생성되는 텍스트의 다양성을 제어하는 역할을 합니다. 온도가 높을수록 모델이 덜 확신하는 경우가 더 자주 발생하므로 샘플링된 텍스트의 다양성이 높아집니다. 반대로, 온도가 낮을수록 모델이 더 확신하는 경우가 더 자주 발생하므로 샘플링된 텍스트의 다양성이 낮아집니다.

예를 들어, 온도가 1.0 이하인 경우 모델이 높은 확률을 가진 문자를 더 자주 선택하게 되므로 생성된 텍스트의 일관성이 높아집니다. 반면에, 온도가 1.0을 초과하는 경우 모델이 낮은 확률을 가진 문자도 선택할 가능성이 높아지므로 생성된 텍스트의 다양성이 높아집니다. 따라서, 온도를 사용하여 샘플링할 때 적절한 값을 선택하여 모델이 생성하는 텍스트의 다양성을 조절할 수 있습니다.

즉, 온도(temperature)는 샘플링 과정에서 생성되는 텍스트의 다양성을 조절하는 하이퍼파라미터입니다.

온도가 낮으면 생성되는 텍스트가 좀 더 일관성 있고, 예측되는 확률이 높은 문자열을 출력합니다. 반면에, 온도가 높을수록 생성되는 텍스트가 더 다양해지고, 예측되는 확률이 낮은 문자열도 출력됩니다. 이렇게 다양성을 조절할 수 있기 때문에, 온도를 적절하게 설정하는 것은 텍스트 생성 모델에서 중요한 문제 중 하나입니다.

예를 들어, 온도가 0.2 이하인 경우에는 확률이 가장 높은 문자를 계속해서 출력하게 됩니다. 이렇게 되면 텍스트가 반복되는 경향이 있어 일관성 있고 예측 가능한 텍스트가 생성됩니다. 반면에, 온도가 1.0 이상인 경우에는 예측된 확률에 따라서 문자열이 더 다양하게 생성됩니다. 예측된 확률이 낮은 문자열도 출력되기 때문에, 일관성 없고 무작위적인 텍스트가 생성될 가능성이 높아집니다.

따라서, 적절한 온도를 선택하여 텍스트 생성 모델의 출력을 조절하는 것은 중요한 과제 중 하나입니다.

 

 

 

 

경사하강법(Gradient Descent)

경사하강법(Gradient Descent)은 최적화 알고리즘 중 하나로, 함수의 최솟값을 찾기 위해 사용됩니다.

함수의 최솟값을 찾는 것은 다양한 분야에서 중요한 문제 중 하나입니다.

가장 기본적인 이유는 함수의 최솟값이 주어졌을 때, 그 함수를 최소화하는 입력값을 찾을 수 있다는 점입니다.

여기서 그래디언트(Gradient)란, 다변수 함수에서 각 변수들의 편미분 값들을 모아놓은 벡터를 의미합니다. 그래디언트는 함수의 기울기와 같은 의미를 가지며, 각 변수의 변화에 따른 함수값의 변화량을 나타냅니다.

경사하강법(Gradient Descent)은 함수의 최솟값을 찾는 최적화 알고리즘 중 하나로, 그래디언트를 이용해 함수의 기울기가 낮아지는 방향으로 일정한 크기의 스텝을 내딛어 최솟값을 찾아가는 방법입니다. 경사하강법은 그래디언트를 이용해 함수의 기울기 정보를 활용하는 방법으로, 최적화 문제를 푸는 데에 그래디언트를 사용할 수 있습니다.

따라서, 그래디언트는 함수의 기울기를 나타내는 벡터이고, 경사하강법은 그래디언트를 이용한 최적화 알고리즘입니다.

이런 경사 하강법은 기울기(Gradient 경사)를 이용하여 함수의 최솟값을 찾아가는 알고리즘이기 때문에, 다양한 분야에서 활용될 수 있습니다.

예를 들어, 어떤 남성이 어떤 여성에게 관심을 가지고 있고, 그 여성의 호감도를 최대화하기 위해서는 어떻게 해야 할까요? 우선, 이 남성은 자신의 매력을 최대한 발휘하기 위해 노력해야 합니다. 즉, 여성이 좋아할만한 자신의 장점을 찾아내고, 그것을 최대한 드러내야 합니다.

이것을 경사 하강법의 관점에서 보면, 남성은 여성이 좋아할만한 자신의 매력을 함수의 값으로 생각할 수 있습니다. 그리고 이 함수의 값이 가장 크게 만드는 요소를 찾아내야 합니다. 이 요소는 여러 가지 요인으로 구성될 수 있습니다. 예를 들어, 남성의 외모, 성격, 재능 등이 그것입니다. 이 중에서 여성이 가장 중요하게 생각하는 것을 찾아서, 그것을 개선해 나가야 합니다.

그리고 이 과정은 여러 번의 시도와 실패를 거쳐서 이루어집니다. 즉, 처음에는 여성이 좋아하는 요소를 정확히 파악하기 어렵기 때문에, 여러 가지 요소를 시도해보고, 그 결과를 측정해보아야 합니다. 이것이 경사 하강법에서 말하는 “기울기를 계산하고, 그 기울기가 가장 낮은 방향으로 이동하는” 과정입니다. 이를 통해 점차적으로 자신의 매력을 극대화할 수 있게 됩니다.

따라서 경사 하강법은 남녀 관계에서도 적용될 수 있으며, 자신의 매력을 최대화하기 위해서는 여러 가지 요소를 시도하고, 그 결과를 측정하여 점차 개선해 나가야 한다는 것을 알 수 있습니다.

또, 일상 생활에서는 스마트폰이나 태블릿 PC 등의 기기를 사용할 때 최적의 배터리 수명을 찾는 것이 경사 하강법과 비슷한 개념으로 생각할 수 있습니다. 배터리 수명은 여러 요인에 의해 영향을 받기 때문에, 이를 최적화하기 위해서는 배터리 사용량, 화면 밝기, Wi-Fi 연결 등 다양한 요소를 고려해야 합니다. 이때 경사 하강법을 이용하여 배터리 사용량과 화면 밝기 등의 요소를 조절해가며, 배터리 수명을 최대한 늘릴 수 있습니다.

또 다른 예로는 운동을 예로 들 수 있습니다. 운동할 때도 목표로 하는 몸매나 체력을 달성하기 위해서는 어떤 운동을 하고, 어떤 식사를 해야할지 고민할 필요가 있습니다. 이때 운동 시간, 식사 메뉴 등의 요소를 경사 하강법을 이용하여 조절하면, 목표하는 몸매나 체력을 달성하는 데 더욱 효과적일 수 있습니다.

경제학에서는 이익을 극대화하기 위한 최적의 가격을 찾는 데 사용되며, 공학에서는 자동차의 에너지 효율성을 최대화하기 위한 최적의 디자인을 찾는 데 사용됩니다.

또한 함수의 최솟값을 찾는 것은 인공지능, 머신러닝, 딥러닝 등의 분야에서도 매우 중요합니다. 이러한 분야에서는 함수의 최솟값을 찾는 것이 최적화 문제의 핵심적인 목표 중 하나이기 때문입니다. 예를 들어, 회귀분석에서는 오차를 최소화하는 모델 파라미터를 찾기 위해, 신경망에서는 손실 함수를 최소화하는 가중치를 찾기 위해, 최적화 알고리즘을 사용하여 함수의 최솟값을 찾습니다.
예를 들어, 선형 회귀 분석에서는 입력값과 출력값 사이의 관계를 나타내는 모델을 학습하는데, 이때 모델의 학습을 위해서는 모델의 예측값과 실제값의 차이인 오차를 최소화하는 최적의 모델 파라미터를 찾아야 합니다. 이때 경사하강법을 사용하여 오차를 최소화하는 최적의 모델 파라미터를 찾을 수 있습니다.

따라서 함수의 최솟값을 찾는 것은 다양한 분야에서 최적화 문제를 해결하기 위해 매우 중요합니다.

경사하강법은 먼저 임의의 시작점에서 시작하여, 현재 위치에서 함수의 기울기(경사)를 계산하고, 그 기울기가 가장 낮은 방향으로 이동합니다. 그리고 그 방향으로 일정 거리만큼 이동한 후에 다시 기울기를 계산하고, 이동합니다. 이 과정을 반복하여, 함수의 최솟값에 도달합니다.

간단한 파이썬 코드는 다음과 같습니다.

def gradient_descent(f, df, x0, lr, max_iter):
    x = x0
    for i in range(max_iter):
        grad = df(x)
        x = x - lr * grad
    return x

여기서 f는 최솟값을 찾으려는 함수이며, df는 f의 기울기(도함수)입니다. x0는 시작점입니다. lr은 학습률(learning rate)이며, 이 값은 한 번의 반복에서 이동할 거리를 결정합니다. max_iter는 최대 반복 횟수입니다.

 

또 아래의 코드는 간단한 2차 함수인 x^2 + y^2를 최소화하는 과정을 경사하강법으로 시각화하는 코드입니다.

실행 결과( Corlab으로 실행하기)

import numpy as np
import matplotlib.pyplot as plt

# 함수 정의
def f(x, y):
    return x**2 + y**2

# 편미분 함수 정의
def df_dx(x, y):
    return 2*x

def df_dy(x, y):
    return 2*y

# 하이퍼파라미터 정의
LEARNING_RATE = 0.1
MAX_ITERATIONS = 50

# 초기값 설정
x = 4.0
y = 4.0

# 경사하강법 알고리즘
for i in range(MAX_ITERATIONS):
    # 현재 위치에서 기울기 계산
    gradient_x = df_dx(x, y)
    gradient_y = df_dy(x, y)
    
    # 경사하강법 업데이트
    x -= LEARNING_RATE * gradient_x
    y -= LEARNING_RATE * gradient_y
    
    # 시각화를 위한 저장
    plt.scatter(x, y, color='red')

# 함수 시각화
x_vals = np.linspace(-6, 6, 100)
y_vals = np.linspace(-6, 6, 100)
x_mesh, y_mesh = np.meshgrid(x_vals, y_vals)
z = f(x_mesh, y_mesh)

plt.contour(x_mesh, y_mesh, z, levels=np.logspace(-1, 3, 10))
plt.colorbar()
plt.show()

Jax를 사용한 간단한 그래디언트 계산

JAX를 사용하면 자동 미분 기능을 활용하여 그래디언트를 간단하게 계산할 수 있습니다.
위 코드에서 그래디언트를 계산하는 부분은 다음과 같습니다.

# 현재 위치에서 기울기 계산
gradient_x = df_dx(x, y)
gradient_y = df_dy(x, y)

df_dx 함수와 df_dy 함수를 이용하여 각각 x, y에 대한 편미분을 계산하고, 이를 gradient_x와 gradient_y 변수에 할당합니다. 이후 경사하강법 알고리즘에서 이 값을 이용하여 모델을 업데이트합니다.
JAX를 사용하면 자동 미분 기능을 활용하여 그래디언트를 간단하게 계산할 수 있습니다.
JAX에서는 grad() 함수를 사용하여 함수의 그래디언트를 계산할 수 있습니다. 따라서 위의 코드는 다음과 같이 변경될 수 있습니다.

실행하기

import jax.numpy as jnp
from jax import grad
import matplotlib.pyplot as plt

# 함수 정의
def f(x):
    return x[0]**2 + x[1]**2

# 하이퍼파라미터 정의
LEARNING_RATE = 0.1
MAX_ITERATIONS = 50

# 초기값 설정
x = jnp.array([4.0, 4.0])

# 그래디언트 계산 함수
grad_f = grad(f)

# 경사하강법 알고리즘
for i in range(MAX_ITERATIONS):
    # 현재 위치에서 그래디언트 계산
    gradient = grad_f(x)
    
    # 경사하강법 업데이트
    x -= LEARNING_RATE * gradient
    
    # 시각화를 위한 저장
    plt.scatter(x[0], x[1], color='red')

# 함수 시각화
x_vals = jnp.linspace(-6, 6, 100)
y_vals = jnp.linspace(-6, 6, 100)
x_mesh, y_mesh = jnp.meshgrid(x_vals, y_vals)
z = f([x_mesh, y_mesh])

plt.contour(x_mesh, y_mesh, z, levels=jnp.logspace(-1, 3, 10))
plt.colorbar()
plt.show()

위 코드에서는 jax.numpy를 jnp라는 별칭으로 사용하고 있으며, 함수 f(x)를 x의 요소 중 첫 번째와 두 번째 요소에 대한 제곱합으로 정의하였습니다. 그리고 jax.grad 함수를 사용하여 f(x)의 그래디언트를 자동으로 계산합니다. 그래디언트를 계산하기 위해 x가 필요하므로, x를 jnp.array로 선언하였습니다. 이후 경사하강법 알고리즘은 이전과 동일하게 작성하였습니다. 마지막으로 함수를 시각화하기 위한 코드는 이전과 동일합니다.

왕초보! 텐서플로우 2.0 머신러닝 기초 강좌

머신러닝과 양자 컴퓨터

양자 컴퓨터는 양자역학의 원리를 기반으로 동작하는 컴퓨터입니다. 기존의 디지털 컴퓨터는 0과 1의 이진수로 정보를 처리하지만, 양자 컴퓨터는 양자 상태를 이용해 병렬 연산을 수행합니다. 이러한 병렬 연산으로 인해, 양자 컴퓨터는 일부 문제에서 기존의 컴퓨터보다 지수적인 속도 향상을 보입니다.

양자 컴퓨터에서는 양자 비트(quantum bit, qubit)라는 개념이 사용됩니다. 양자 비트는 일반적인 비트와는 달리, 0과 1이 아닌, 양자 상태를 갖습니다. 이러한 양자 상태는 측정하기 전까지는 모든 가능한 상태를 동시에 갖을 수 있습니다. 이를 이용해 병렬 연산을 수행하고, 양자 알고리즘을 통해 복잡한 계산 문제를 해결할 수 있습니다.

하지만, 양자 컴퓨터는 아직까지 기술적으로 매우 어렵고, 구현도 매우 어려운 분야입니다. 현재까지는 제한된 크기의 양자 컴퓨터만 구현되어 있으며, 알고리즘도 한계가 있습니다. 그러나 양자 컴퓨터 기술의 발전과 함께 미래에는 더욱 강력한 양자 컴퓨터가 등장할 가능성이 있습니다.

현재까지 양자 컴퓨터를 이용해 작성된 프로그램에는 다음과 같은 것들이 있습니다.

양자 알고리즘: 퀀텀 텔레포테이션, 양자 계산복잡도 이론 등
양자 암호화: 양자 키 분배, 양자 암호 해독 등
양자 머신러닝: 양자 뉴럴 네트워크, 양자 지도학습, 양자 강화학습 등
양자 시뮬레이션: 양자 액체, 양자 자기장 등의 시뮬레이션
양자 화학: 분자 시뮬레이션 등
이외에도 연구가 계속 진행되고 있으며, 양자 컴퓨터를 이용한 새로운 분야와 애플리케이션이 발견될 가능성이 매우 높습니다.