이번에 정리할 글들은 모두 Neural Network를 이해하기 전에 필요한 기초지식들이다.

생각보다,,,인공지능을 만들 때 필요한 개념들은 모두 뇌의 구조에서 따왔다는 것을 볼 수 있다.
입력 신호를 받아 이 신호를 증폭(w값을 사용하여)시키고 편향값을 더한 후 신호들의 합을 출력 신호로 출력하는
뉴런의 모습은 지금까지 우리가 tensorflow를 이용해 개발한 hypothesis와 매우 유사해보인다!
수많은 뉴런이 엮어져 만들어진 뇌의 구조를 본따 인공지능을 만들 수 있을 것 같아 보이는데,
이는 사실 실제로 가능한 일이 되었다는 점을 아래 사진을 보면 알 수 있다.

위와 같은 아이디어는 xor 문제를 해결하기 위해 탄생했다.
xor의 특징은 하나의 linear한 hypothesis로 해결하기 까다롭다는 점이다.

인공지능 분야의 유명한 교수인 민스키 교수가 xor 문제의 해결책을 제시하였는데 그것이 바로,
MLP(multi-layer perceptrons)
...를 사용해야한다는 것이었다.
그러나 그는 가중치(w)와 편향도(b)를 구하는 것은 사실상 불가능에 가깝다고 말했다.
즉, 위와 같은 MLP를 학습시키는 방법은 없다는 뜻이었다.

그러나 Paul Werbos가 처음 논문을 쓰고, Hinton이 재발견한,
Back propagation
...을 통해 위의 w, b를 구하는 것이 불가능한 것이 아님을 입증했다!

Paul이 처음 주장했지만, 인기를 얻지 못했다고 한다... 불쌍...ㅠㅜ
network를 통해 얻은 출력값을 실제 Layer와 비교해보고 다르다면
network를 반대로 올라가면서 w(가중치, weight)과 b(편향도, bias)를 수정하는 방법을 만들어낸 것이다!
MLP를 구현하는 것이 가능해졌다! 😊
인간은 여러 분야에 이를 적용하기 시작했고, 사진을 구별하는 Machine Learning을 구현해보고자 했다.
이때 유심히 봐야하는 실험이 있는데, 그 실험은 바로 "고양이 실험"이다.

놀라운 점은 그림의 일부를 고양이에게 보여줄 때 활성화되는 신경들이 존재한다는 것이었다.
즉, 그림을 볼때 전체를 보는 것이 아니라 사실은 일부분을 부분부분 보며 그 물체를 판단한다는 점을 발견해냈다!
이것이 CNN의 등장이 되었다!

그러나 Back propagation의 문제점이 드러나기 시작했는데, 적은 수의 퍼셉트론에서는 성능이 잘 나오지만,
많은 수의 퍼셉트론이 존재할 경우 앞의 퍼셉트론에 w, b의 의미가 약해지면서 성능이 떨어지는 현상이 발생했다.
즉, 간단한 Neural Network가 더 성능이 잘 나온다는 것을 발견했고,
(CNN의 성능 < SVM, Random forest의 성능)이라는 점을 보였다.
[Breakthrough 논문]에서 소개된 3가지 내용을 소개하겠다.
1. W의 초기값을 잘 주는 것이 가장 중요함(랜덤보다는...)
2. 복잡한 방법보단 단순한 방식의 방법이 더 효율적이다.
3. Deep learning으로 이름을 리브랜딩하는 것이 더 좋다.
이후, CIFAR의 Alex라는 사람이 CNN을 통해 Image Classification에서 놀라운 성능 향상을 보이며,
사진 분류 뿐만아니라 사진의 상황 설명, 그리고 API를 자동으로 사용하도록 해주는 AI 개발까지 진행될 수 있었다.
[Hinton의 과거 실패한 요인에 대한 고찰 4가지]를 살펴보면 다음과 같다.
1. 라벨링된 데이터의 개수가 너무 적음
2. 컴퓨터의 성능이 너무 안좋았음
3. W를 바보같은 방식으로 두었음(아마도 랜덤이나 무지성으로 가까운 값으로 설정했을 듯?)
4. 선형적이지 않은 모습의 공식을 사용함
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
import numpy as np
print("==================================================")
sess = tf.Session()
t = tf.constant([ #axis = 0
[ #axis = 1
[ #axis = 2
[1, 2, 3, 4], #axis = 3
[5, 6, 7, 8],
[9, 10, 11, 12]
],
[
[13, 14, 15, 16],
[17, 18, 19, 20],
[21, 22, 23, 24]
]
]
])
sess.run(tf.shape(t))
print("==================================================")
# rank는 차원이라고 생각하면 되므로 4차원, shape은 이 배열의 모습이라는 뜻이므로 [1, 2, 3, 4]가 됨
# axis는 중심선, 축이라는 의미로 axis의 index는 shape의 값을 차례로 가지게 됨
# 즉, 위 배열에서 axis가 0이라면 1이되고, axis가 2라면 3이 되는 것
# ex) shape = [1, 2, 3, 4], axis0 == shape[0], axis2 == shape[2]
# 아래의 reduce sum 연산을 통해 axis의 뜻을 좀 더 잘 파악할 수 있음
matrix1 = tf.constant([[1, 2], [3, 4]])
matrix2 = tf.constant([[1], [2]])
print("Metrix 1 shape", matrix1.shape)
print("Metrix 2 shape", matrix2.shape)
print(sess.run(tf.matmul(matrix1, matrix2)))
print(sess.run(matrix1 * matrix2)) #warning!
matrix1 = tf.constant([[3, 3]])
matrix2 = tf.constant([[2, 2]])
print(sess.run(matrix1 + matrix2))
# BroadCasting에 유의해야함
# 앞의 shape에 맞춰 확장하므로 주의!
matrix1 = tf.constant([1, 2])
matrix2 = tf.constant(3)
print(sess.run(matrix1 + matrix2))
# ex) [1, 2] + 3 => [1, 2] + [3, 3] => [4, 5]
# 여러 상황이 존재하므로 shape은 맞춰주는 것이 좋음
print("==================================================")
# reduce_mean은 axis를 기입할 경우 axis를 기준으로 평균을 구하게 됨
print(sess.run(tf.reduce_mean([1, 2], axis=0)))
x = [[1, 2],
[3, 4]]
print(sess.run(tf.reduce_mean(x)))
# axis가 없다면 모든 원소의 평균을 구함
print(sess.run(tf.reduce_mean(x, axis=0)))
print(sess.run(tf.reduce_mean(x, axis=1))) #axis = -1과 같음
print("==================================================")
# reduce_sum은 axis를 기입할 경우 해당 axis를 없애는 방향으로 연산을 수행함
print(sess.run(tf.reduce_sum(t, axis=0)))
print(sess.run(tf.reduce_sum(t, axis=1)))
print(sess.run(tf.reduce_sum(t, axis=2)))
print(sess.run(tf.reduce_sum(t, axis=3))) #axis = -1과 같음
# 위 계산을 통해 axis를 더 잘 이해할 수 있음
print("==================================================")
# argmax는 axis를 기준으로 가장 큰 값의 index값을 반환함
x = [[0, 1, 2],
[2, 1, 0]]
print(sess.run(tf.argmax(x, axis=0)))
print(sess.run(tf.argmax(x, axis=1)))
print("==================================================")
# reshape는 뭔가 알잘딱 하게 format을 맞춰주는 것 같음
# 왠진 모르겠지만 이해는 됨
# 보통 마지막 shape은 남겨두고 앞의 수를 -1로 둔 후 중간에 shape을 바꿔줌
t = np.array([[[0, 1, 2],
[3, 4, 5]],
[[6, 7, 8],
[9, 10, 11]]])
print(sess.run(tf.reshape(t, shape=[-1, 3])))
print(sess.run(tf.reshape(t, shape=[-1, 1, 3])))
# 하나의 배열로 합체
print(sess.run(tf.squeeze([[0], [1], [2]])))
# 차원을 인자의 개수 만틈 증가
print(sess.run(tf.expand_dims([0, 1, 2], 1)))
print("==================================================")
# one hot을 해주는 one_hot function이 존재함
# 보통 reshape와 함께 사용하여 우리가 원하는 Y format을 얻음
# 이유는 one hot으로 만들면서 차원이 하나 늘어나버리기 때문임
t = [[0], [1], [2], [0]]
# depth는 표현하고자 하는 객체의 수임
# 지금은 0, 1, 2를 분류해야하므로 depth=3을 줌
print(sess.run(tf.one_hot(t, depth=3)))
print(sess.run(tf.reshape(t, shape=[-1, 3])))
print("==================================================")
# cast도 가능함
print(sess.run(tf.cast([1.8, 2.2, 3.3, 4.9], tf.int32)))
print(sess.run(tf.cast([True, False], tf.int32)))
print("==================================================")
# stack을 사용하여 리스트를 하나의 리스트로 쌓는 것이 가능함
x = [1, 4]
y = [2, 5]
z = [3, 6]
print(sess.run(tf.stack([x, y, z])))
print(sess.run(tf.stack([x, y, z], axis=1)))
print("==================================================")
# ones and zero like를 통해 1, 0으로 채워진 동일한 차원의 배열을 생성할수도 있음
x = [[0, 1, 2],
[2, 1, 0]]
print(sess.run(tf.ones_like(x)))
print(sess.run(tf.zeros_like(x)))
print("==================================================")
# zip을 통해 2개의 배열을 동시에 for으로 전달 가능함
for x, y, z in zip([1, 2, 3], [4, 5, 6], [7, 8, 9]):
print(x, y, z)
print("==================================================")
'AI > 모두를 위한 딥러닝 정리' 카테고리의 다른 글
Day10. Activation Functions, weight initialization, Dropout and Ensemble (0) | 2022.04.20 |
---|---|
Day9. Neural Network and TensorBoard code (0) | 2022.04.19 |
Day7. learning rate, overfitting, regularization (0) | 2022.04.16 |
Day6. Softmax (0) | 2022.04.11 |
Day5. Logistic Classification과 Logistic Regression (0) | 2022.04.01 |
댓글