퍼셉트론은 프랑크 로젠블라트(Frank Rosenblatt)가 고안한 인공 신경망으로 실제 뇌의 뉴런과 비슷하게 동작합니다.

퍼셉트론 모델은 여러 입력값을 받아 가중치를 곱한 뒤 합하여 활성함수에 넣어 결과를 얻어냅니다.

이 간단한 단층 퍼셉트론을 이용하여 비트연산을 학습시켜 보겠습니다.

학습시키기 전에 학습시킬 데이터를 생성해야 하기 때문에 데이터를 생성해주는 함수를 만들겠습니다.

def generate_or_data():
    x = np.random.randint(0, 2, size=(100, 2))
    y = np.array([x[:, 0] | x[:, 1]]).T
    return x, y

def generate_and_data():
    x = np.random.randint(0, 2, size=(100, 2))
    y = np.array([x[:, 0] & x[:, 1]]).T
    return x, y

def generate_xor_data():
    x = np.random.randint(0, 2, size=(100, 2))
    y = np.array([x[:, 0] ^ x[:, 1]]).T
    return x, y

def plot_data(x, y):
    plt.scatter(x[:, 0], x[:, 1], c=y[:, 0], cmap=plt.cm.bwr)
    plt.show()

plot_data(*generate_or_data())
plot_data(*generate_and_data())
plot_data(*generate_xor_data())

OR

class Model(tf.Module):
    def __init__(self):
        self.w = tf.Variable(tf.random.uniform(shape=[2, 1], minval=-1, maxval=1))
        self.b = tf.Variable(tf.random.uniform(shape=[1], minval=-1, maxval=1))

    def __call__(self, x):
        y = tf.sigmoid(tf.matmul(x, self.w) + self.b)
        return y

def loss_fn(x, y):
    return tf.reduce_mean(tf.square(model(x) - y))

학습시킬 모델과 손실함수를 정의 해 주겠습니다.

비트연산자는 두개의 입력을 받아 하나의 출력을 내보내기 때문에

가중치는 (2, 1) 형태의 배열이 필요합니다.

model = Model()

def train(x, y, lr=0.1):
    with tf.GradientTape() as tape:
        loss = loss_fn(x, y)
    grads = tape.gradient(loss, [model.w, model.b])
    model.w.assign_sub(lr * grads[0])
    model.b.assign_sub(lr * grads[1])

def predict(x):
    return model(x)

def accuracy(x, y):
    return tf.reduce_mean(tf.cast(tf.equal(tf.round(predict(x)), y), tf.float32))

모델을 생성하고, 학습을 시켜줄 경사하강법 알고리즘으로 작성된 함수를 만들어 줍니다.

그 뒤 모델의 정확도를 구하기 위하여 정확도를 계산해주는 함수를 만들어 줍니다.

x, y = generate_or_data()

x = tf.constant(x, dtype=tf.float32)
y = tf.constant(y, dtype=tf.float32)

plot_data(x, y)

for i in range(1000):
    train(x, y)
    if i % 100 == 0:
        print('loss: {:.4f}, accuracy: {:.4f}'.format(loss_fn(x, y), accuracy(x, y)))

데이터를 생성하고 학습시키면 정확도가 1이 나올정도로 정확하게 학습됐다는건 알 수 있게 됐습니다.

어떤 방식으로 값을 판단하는지 알아보기 위해 출력되는 값의 정도를 투명도로 정해서 어떻게 나오는지 보겠습니다.

def plot_decision_boundary():
    x = np.linspace(0, 1, 100)
    y = np.linspace(0, 1, 100)
    x = tf.constant(x, dtype=tf.float32)
    y = tf.constant(y, dtype=tf.float32)
    xx, yy = np.meshgrid(x, y)
    z = predict(np.c_[xx.ravel(), yy.ravel()])
    z = z.numpy().reshape(xx.shape)
    plt.contourf(xx, yy, z, cmap=plt.cm.bwr, alpha=0.2)
    plt.show()

plot_decision_boundary()

AND

or 코드를 가져다 사용하겠습니다.

model = Model()

x, y = generate_and_data()

x = tf.constant(x, dtype=tf.float32)
y = tf.constant(y, dtype=tf.float32)

plot_data(x, y)

for i in range(1000):
    train(x, y)
    if i % 100 == 0:
        print('loss: {:.4f}, accuracy: {:.4f}'.format(loss_fn(x, y), accuracy(x, y)))

plot_decision_boundary()

변한건 데이터를 생성해주는게 or에서 and로 변한 것 밖에 없습니다.

학습시켜보면 아까전 or과 다르게 (0, 1), (1, 0) 일때가 빨간색에서 파란색으로 변했습니다.

이걸로 AND도 학습이 제대로 되었다는걸 볼 수 있습니다.

XOR

model = Model()

x, y = generate_xor_data()

x = tf.constant(x, dtype=tf.float32)
y = tf.constant(y, dtype=tf.float32)

plot_data(x, y)

for i in range(10000):
    train(x, y)
    if i % 100 == 0:
        print('loss: {:.4f}, accuracy: {:.4f}'.format(loss_fn(x, y), accuracy(x, y)))

plot_decision_boundary()

데이터를 xor로 바꿔서 학습시켜 보겠습니다. 학습도 10배 더 늘렸습니다.

이번엔 정확도도 0.8로 나오고 판정 기준도 뭔가 이상한걸 볼 수 있습니다.

단층 퍼셉트론으로는 XOR 문제를 해결할 수 없기 때문입니다.

단층 퍼셉트론은 최고차항이 1로 일차 함수인데 일차 함수로는 아무리 선을 잘 그어봐도 XOR을 판정을 내릴 수 없습니다.

다층 퍼셉트론을 이용해서 XOR 문제를 풀어보겠습니다.

class Layer(tf.Module):
    def __init__(self, out_dim, weight_init=tf.random.uniform, activation=tf.identity):
        self.out_dim = out_dim
        self.weight_init = weight_init
        self.activation = activation
        self.w = None
        self.b = None

    @tf.function
    def __call__(self, x):
        self.in_dim = x.shape[1]
        if self.w is None:
            self.w = tf.Variable(self.weight_init(shape=[self.in_dim, self.out_dim]))
        if self.b is None:
            self.b = tf.Variable(tf.zeros(shape=[self.out_dim]))
        z = tf.add(tf.matmul(x, self.w), self.b)
        return self.activation(z)

class MLP(tf.Module):
    def __init__(self, layers):
        self.layers = layers

    @tf.function
    def __call__(self, x, preds=False): 
        for layer in self.layers:
            x = layer(x)
        return x

model = MLP([
    Layer(2, activation=tf.sigmoid),
    Layer(1, activation=tf.sigmoid)
])

x, y = generate_xor_data()

x = tf.constant(x, dtype=tf.float32)
y = tf.constant(y, dtype=tf.float32)

plot_data(x, y)

def accuracy(x, y):
    return tf.reduce_mean(tf.cast(tf.equal(tf.round(model(x)), y), tf.float32))

def loss_fn(x, y):
    return tf.reduce_mean(tf.square(model(x) - y))

def train(x, y, lr=0.1):
    with tf.GradientTape() as tape:
        loss = loss_fn(x, y)
    optimizer = tf.keras.optimizers.Adam(learning_rate=lr)
    grads = tape.gradient(loss, model.trainable_variables)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))

def plot_decision_boundary():
    x = np.linspace(0, 1, 100)
    y = np.linspace(0, 1, 100)
    x = tf.constant(x, dtype=tf.float32)
    y = tf.constant(y, dtype=tf.float32)
    xx, yy = np.meshgrid(x, y)
    z = model(np.c_[xx.ravel(), yy.ravel()])
    z = z.numpy().reshape(xx.shape)
    print(z)
    plt.contourf(xx, yy, z, cmap=plt.cm.bwr, alpha=0.2)
    plt.show()

for i in range(1000):
    train(x, y)
    if i % 100 == 0:
        print('loss: {:.4f}, accuracy: {:.4f}'.format(loss_fn(x, y), accuracy(x, y)))

plot_decision_boundary()

Gradient Descent를 사용하면 학습이 잘 되지 않아 Adam Optimizer를 사용했습니다.

아주 아름답게 학습되서 양쪽 끝만 빨간색인걸 볼 수 있습니다.

이러한 것도 볼 수 있습니다.

BitwisePerceptron.ipynb
0.15MB

728x90

'인공지능' 카테고리의 다른 글

선형 회귀(Linear Regression)  (1) 2022.10.28

+ Recent posts