20200428 - 파이썬 머신러닝(Tensorflow, 이미지 분석, 영화 리뷰 텍스트 분석(긍정|부정 평가)
## 첫 번째 신경망 훈련하기: 기초적인 분류 문제 # https://www.tensorflow.org/tutorials/keras/classification?hl=ko # tensorflow와 tf.keras 를 임포트 합니다. import tensorflow as tf from tensorflow import keras # 헬퍼 라이브러리를 임포트합니다. import numpy as np import matplotlib.pyplot as plt #패션 MNIST 데이터셋 임포트하기 # 10개의 범주와 70,000개의 흑백 이미지로 구성된 패션 MNIST 데이터셋을 사용 fashion_mnist = keras.datasets.fashion_mnist # 훈련용 이미지와 라벨, 테스트용 이미지와 라벨 4가지로 분류해준다. (train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data() # 패션 데이터셋 내부가 파이썬 코드로 되어있기 때문
'''
load_data() 함수를 호출하면 네 개의 넘파이(NumPy) 배열이 반환됩니다:
train_images와 train_labels 배열은 모델 학습에 사용되는 훈련 세트입니다.
test_images와 test_labels 배열은 모델 테스트에 사용되는 테스트 세트입니다.
이미지는 28x28 크기의 넘파이 배열이고 픽셀 값은 0과 255 사이입니다.
레이블(label)은 0에서 9까지의 정수 배열입니다. (0 : T-shirt/top, 1 : 1 : Trouser 등)
이 값은 이미지에 있는 옷의 클래스(class)를 나타냅니다:
각 이미지는 하나의 레이블에 매핑되어 있습니다.
데이터셋에 클래스 이름이 들어있지 않기 때문에 나중에
이미지를 출력할 때 사용하기 위해 별도의 변수를 만들어 저장합니다:
'''
# 0~9까지에 해당하는 아이템들을 표기해주기 위함 class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat', 'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'] #데이터 탐색 #모델을 훈련하기 전에 데이터셋 구조를 살펴보죠. 다음 코드는 훈련 세트에 #60,000개의 이미지가 있다는 것을 보여줍니다. 각 이미지는 28x28 픽셀로 표현됩니다: train_images.shape # (60000, 28, 28) 60,000개 28x28 사이즈 len(train_labels) # 60,000개의 라벨 train_labels # array([9, 0, 0, ..., 3, 0, 5], dtype=uint8) : uint의 unsigned(양의 정수) test_images.shape # (10000, 28, 28) 10,000개 28x28 사이즈 len(test_labels) # 10,000개의 라벨 # 데이터 전처리 # 네트워크를 훈련하기 전에 데이터를 전처리해야 합니다. # 훈련 세트에 있는 첫 번째 이미지를 보면 픽셀 값의 범위가 0~255 사이라는 것을 알 수 있습니다: plt.figure() plt.imshow(train_images[0]) # 이미지 출력해주는 함수. [0]번 이미지 plt.colorbar() # 색상 바 만들어주기 plt.grid(False) # 그리드는 표시하지 않음 plt.show() # 보여주기
대표사진 삭제
사진 설명을 입력하세요.
#신경망 모델에 주입하기 전에 이 값의 범위를 0~1 사이로 조정하겠습니다. #이렇게 하려면 255로 나누어야 합니다. #훈련 세트와 테스트 세트를 동일한 방식으로 전처리하는 것이 중요합니다: train_images = train_images / 255.0 # 훈련용 이미지 60000개를 각각 바꿔준다. test_images = test_images / 255.0 # 테스트용 이미지 10000개를 각각 바꿔준다.
#훈련 세트에서 처음 25개 이미지와 그 아래 클래스 이름을 출력해 보죠. #데이터 포맷이 올바른지 확인하고 네트워크 구성과 훈련할 준비를 마칩니다. plt.figure(figsize=(10,10)) # 차트의 기본 사이즈를 10,10 으로 만든다(비율값) for i in range(25): # 25번 돌게 만든다.(마지막 크기값에서 하나 뺀 것 0~24번 정수형태로 잡기때문에 25번 돈다.) plt.subplot(5,5,i+1) # 25개를 만들어준다. 5x5 차트. 하나를 그린 후 다음 위치를 조정해주기 위해 i+1 옵션 추가 plt.xticks([]) #틱 만들지 않는다. plt.yticks([]) plt.grid(False) #그리드 만들지 않는다. plt.imshow(train_images[i], cmap=plt.cm.binary) # 처음은 0 번째 이미지를 보여달라, cmap은 컬러맵 plt.xlabel(class_names[train_labels[i]]) # x축에다 라벨 붙이는데 트레인 라벨값의 0 번째 값 plt.show() # 보여주기
전송중...
사진 설명을 입력하세요.
#모델 구성 #층 설정 ''' 신경망의 기본 구성 요소는 층(layer)입니다. 층은 주입된 데이터에서 표현을 추출합니다. 아마도 문제를 해결하는데 더 의미있는 표현이 추출될 것입니다. 대부분 딥러닝은 간단한 층을 연결하여 구성됩니다. tf.keras.layers.Dense와 같은 층들의 가중치(parameter)는 훈련하는 동안 학습됩니다. ''' # 총 3개의 층을 만든다. model = keras.Sequential([ keras.layers.Flatten(input_shape=(28, 28)), # 플랫 : 하나의 이미지가 28x28 픽셀(배열) 플랫은 이런 2차원 배열 형태를 1차원으로 펼쳐준다. keras.layers.Dense(128, activation='relu'), # 128은 노드들에 대한 밀집값(연산의 개수), relu 내장 알고리즘 keras.layers.Dense(10, activation='softmax') # 10은 연산이 10개 있다. , softmax 내장 알고리즘(10개의 확률(라벨값이 0~9 10개이기때문에)을 반환시켜줌, 반환값의 전체합은 1) ]) ''' 이 네트워크의 첫 번째 층인 tf.keras.layers.Flatten은 2차원 배열(28 x 28 픽셀)의 이미지 포맷을 28 * 28 = 784 픽셀의 1차원 배열로 변환합니다. 이 층은 이미지에 있는 픽셀의 행을 펼쳐서 일렬로 늘립니다. 이 층에는 학습되는 가중치가 없고 데이터를 변환하기만 합니다. 픽셀을 펼친 후에는 두 개의 tf.keras.layers.Dense 층이 연속되어 연결됩니다. 이 층을 밀집 연결(densely-connected) 또는 완전 연결(fully-connected) 층이라고 부릅니다. 첫 번째 Dense 층은 128개의 노드(또는 뉴런)를 가집니다. 두 번째 (마지막) 층은 10개의 노드의 소프트맥스(softmax) 층입니다. 이 층은 10개의 확률을 반환하고 반환된 값의 전체 합은 1입니다. 각 노드는 현재 이미지가 10개 클래스 중 하나에 속할 확률을 출력합니다. (10개의 확률 중 가장 높은 확률을 가진 것이 해당 의류) '''
#모델 컴파일 #모델을 훈련하기 전에 필요한 몇 가지 설정이 모델 컴파일 단계에서 추가됩니다: # 케라스 내부 내장 함수(아래 3 가지는 반복학습을 할 경우에 지표들을 이용해서 모니터링 할 수 있다.) #손실 함수(Loss function)-훈련 하는 동안 모델의 오차를 측정합니다. 모델의 학습이 올바른 방향으로 향하도록 이 함수를 최소화해야 합니다. #옵티마이저(Optimizer)-데이터와 손실 함수를 바탕으로 모델의 업데이트 방법을 결정합니다. #지표(Metrics)-훈련 단계와 테스트 단계를 모니터링하기 위해 사용합니다. 다음 예에서는 올바르게 분류된 이미지의 비율인 정확도를 사용합니다. model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
#모델 훈련 #신경망 모델을 훈련하는 단계는 다음과 같습니다: ''' 훈련 데이터를 모델에 주입합니다-이 예에서는 train_images와 train_labels 배열입니다. 모델이 이미지와 레이블을 매핑하는 방법을 배웁니다. 테스트 세트에 대한 모델의 예측을 만듭니다-이 예에서는 test_images 배열입니다. 이 예측이 test_labels 배열의 레이블과 맞는지 확인합니다. 훈련을 시작하기 위해 model.fit 메서드를 호출하면 모델이 훈련 데이터를 학습합니다: ''' model.fit(train_images, train_labels, epochs=5) #모델이 훈련되면서 손실과 정확도 지표가 출력됩니다. 이 모델은 훈련 세트에서 약 0.90(90%) 정도의 정확도를 달성합니다.
Epoch 1/5 1875/1875 [==============================] - 4s 2ms/step - loss: 0.2791 - accuracy: 0.8961 Epoch 2/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.2643 - accuracy: 0.9010 Epoch 3/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.2566 - accuracy: 0.9035 Epoch 4/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.2459 - accuracy: 0.9080 Epoch 5/5 1875/1875 [==============================] - 3s 2ms/step - loss: 0.2371 - accuracy: 0.9101 <tensorflow.python.keras.callbacks.History at 0x7fd00fec4ac8>
#정확도 평가 #그다음 테스트 세트에서 모델의 성능을 비교합니다: test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2) print('\n테스트 정확도:', test_acc) ''' 테스트 세트의 정확도가 훈련 세트의 정확도보다 조금 낮습니다. 훈련 세트의 정확도와 테스트 세트의 정확도 사이의 차이는 과대적합(overfitting) 때문입니다. 과대적합은 머신러닝 모델이 훈련 데이터보다 새로운 데이터에서 성능이 낮아지는 현상을 말합니다. '''
313/313 - 0s - loss: 0.3682 - accuracy: 0.8677 테스트 정확도: 0.8676999807357788 '\n테스트 세트의 정확도가 훈련 세트의 정확도보다 조금 낮습니다.\n 훈련 세트의 정확도와 테스트 세트의 정확도 사이의 차이는 과대적합(overfitting) 때문입니다.\n 과대적합은 머신러닝 모델이 훈련 데이터보다 새로운 데이터에서 성능이 낮아지는 현상을 말합니다.\n'
#예측 만들기 #훈련된 모델을 사용하여 이미지에 대한 예측을 만들 수 있습니다. predictions = model.predict(test_images) #여기서는 테스트 세트에 있는 각 이미지의 레이블을 예측했습니다. 첫 번째 예측을 확인해 보죠: predictions[0] # 가장 높은 확률이 6.9915450e-01 으로 9번인덱스의 확률이 가장 높다.
array([1.8291599e-06, 2.7936718e-08, 6.1906564e-07, 1.3683453e-08, 1.2535853e-07, 2.4993978e-01, 1.0020217e-06, 5.0897580e-02, 4.4605044e-06, 6.9915450e-01], dtype=float32)
#이 예측은 10개의 숫자 배열로 나타납니다. #이 값은 10개의 옷 품목에 상응하는 모델의 신뢰도(confidence)를 나타냅니다. 가장 높은 신뢰도를 가진 레이블을 찾아보죠: np.argmax(predictions[0]) # 배열을 꺼낼때마다 항상 셀 수 없으니 가장 큰 값의 인덱스를 꺼내온다.
9
#모델은 이 이미지가 앵클 부츠(class_name[9])라고 가장 확신하고 있습니다. 이 값이 맞는지 테스트 레이블을 확인해 보죠: test_labels[0]
9
#10개 클래스에 대한 예측을 모두 그래프로 표현해 보겠습니다: # 이미지 그려주는 함수 def plot_image(i, predictions_array, true_label, img): # i값 받고, 예측결과 받고, 정답지 받고, 이미지 받는다. predictions_array, true_label, img = predictions_array[i], true_label[i], img[i] plt.grid(False) plt.xticks([]) plt.yticks([]) plt.imshow(img, cmap=plt.cm.binary) predicted_label = np.argmax(predictions_array) if predicted_label == true_label: color = 'blue' else: color = 'red' plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label], # 라벨 뿌려주는 부분 100*np.max(predictions_array), class_names[true_label]), color=color) # 바 그래프 그려주는 함수 def plot_value_array(i, predictions_array, true_label): predictions_array, true_label = predictions_array[i], true_label[i] plt.grid(False) plt.xticks([]) plt.yticks([]) thisplot = plt.bar(range(10), predictions_array, color="#777777") plt.ylim([0, 1]) predicted_label = np.argmax(predictions_array) thisplot[predicted_label].set_color('red') thisplot[true_label].set_color('blue')
#0번째 원소의 이미지, 예측, 신뢰도 점수 배열을 확인해 보겠습니다. i = 0 # i값을 999로 넣게되면 999번째 이미지에 대한 예측값이 나오게 된다. plt.figure(figsize=(6,3)) plt.subplot(1,2,1) # 1x2 행렬에서 1번 차트 plot_image(i, predictions, test_labels, test_images) plt.subplot(1,2,2) # 1x2 행렬에서 2번 차트 plot_value_array(i, predictions, test_labels) plt.show()
전송중...
사진 설명을 입력하세요.
i = 12 plt.figure(figsize=(6,3)) plt.subplot(1,2,1) plot_image(i, predictions, test_labels, test_images) plt.subplot(1,2,2) plot_value_array(i, predictions, test_labels) plt.show()
전송중...
사진 설명을 입력하세요.
# 몇 개의 이미지의 예측을 출력해 보죠. 올바르게 예측된 레이블은 파란색이고 잘못 예측된 레이블은 빨강색입니다. # 숫자는 예측 레이블의 신뢰도 퍼센트(100점 만점)입니다. 신뢰도 점수가 높을 때도 잘못 예측할 수 있습니다. # 처음 X 개의 테스트 이미지와 예측 레이블, 진짜 레이블을 출력합니다 # 올바른 예측은 파랑색으로 잘못된 예측은 빨강색으로 나타냅니다 num_rows = 5 num_cols = 3 num_images = num_rows*num_cols # 범위변수는 5x3 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) plot_image(i, predictions, test_labels, test_images) plt.subplot(num_rows, 2*num_cols, 2*i+2) plot_value_array(i, predictions, test_labels) plt.show() # 이미지와 그래프가 한 쌍인 5x3 행렬
전송중...
사진 설명을 입력하세요.
# 테스트 세트에서 이미지 하나를 선택합니다 img = test_images[0] print(img.shape) # tf.keras 모델은 (예측할) 한 번에 샘플의 묶음(배치(batch))으로 예측을 만드는데 최적화되어 있습니다. # 하나의 이미지를 사용할 때에도 2차원 배열로 만들어야 합니다:
(28, 28)
# 이미지 하나만 사용할 때도 배치에 추가합니다 img = (np.expand_dims(img,0)) print(img.shape)
(1, 28, 28)
#이제 이 이미지의 예측을 만듭니다: predictions_single = model.predict(img) print(predictions_single) # 예측값 10개
[[8.7910990e-07 5.6614036e-10 7.3793046e-07 2.0874624e-10 1.1568890e-08 1.5215304e-03 1.4093014e-07 8.8366279e-03 2.4851545e-07 9.8963988e-01]]
plot_value_array(0, predictions_single, test_labels) _ = plt.xticks(range(10), class_names, rotation=45) # x축에다가 클래스명들을 출력할건데 45도 기울기를 줘서 출력한다. # _ 이거는 차트에다가 덧그리는것이기 때문에 _ 이대로 표시(덧그린다는 뜻)
전송중...
사진 설명을 입력하세요.
#model.predict는 2차원 넘파이 배열을 반환하므로 첫 번째 이미지의 예측을 선택합니다: np.argmax(predictions_single[0]) #이전과 마찬가지로 모델의 예측은 레이블 9입니다.
9
# https://www.tensorflow.org/tutorials/keras/text_classification?hl=ko # 이 예제는 이진 또는 클래스가 두개인 분류 문제입니다. # 이진 분류는 머신러닝에서 중요하고 널리 사용됩니다. # 여기에서는 인터넷 영화 데이터베이스에서 수집한 50,000개의 영화 리뷰 텍스트를 담은 IMDB 데이터셋을 사용하겠습니다. # 25,000개 리뷰는 훈련용으로, 25,000개는 테스트용으로 나뉘어져 있습니다. # 훈련 세트와 테스트 세트의 클래스는 균형이 잡혀 있습니다. # 즉, 긍정적인 리뷰와 부정적인 리뷰의 개수가 동일합니다. # tensorflow와 tf.keras 를 임포트 합니다. import tensorflow as tf from tensorflow import keras # 헬퍼 라이브러리를 임포트합니다. import numpy as np import matplotlib.pyplot as plt
#IMDB 데이터셋 다운로드 #IMDB 데이터셋은 텐서플로와 함께 제공됩니다. 리뷰(단어의 시퀀스(sequence))는 미리 #전처리해서 정수 시퀀스로 변환되어 있습니다. 각 정수는 [어휘 사전에 있는 특정 단어를 의미]합니다. #다음 코드는 IMDB 데이터셋을 컴퓨터에 다운로드합니다(또는 이전에 다운로드 받았다면 캐시된 복사본을 사용합니다): imdb = keras.datasets.imdb (train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000) # 매개변수 num_words=10000은 훈련 데이터에서 가장 많이 등장하는 상위 10,000개의 단어를 선택합니다. # 데이터 크기를 적당하게 유지하기 위해 드물에 등장하는 단어는 제외하겠습니다.
#데이터 탐색 #잠시 데이터 형태를 알아 보죠. 이 데이터셋의 샘플은 전처리된 정수 배열입니다. #이 정수는 영화 리뷰에 나오는 단어를 나타냅니다. #레이블(label)은 정수 0 또는 1입니다. 0은 부정적인 리뷰이고 1은 긍정적인 리뷰입니다. print("훈련 샘플: {}, 레이블: {}".format(len(train_data), len(train_labels)))
훈련 샘플: 25000, 레이블: 25000
#리뷰 텍스트는 어휘 사전의 특정 단어를 나타내는 정수로 변환되어 있습니다. 첫 번째 리뷰를 확인해 보죠: print(train_data[0])
[1, 14, 22, 16, 43, 530, 973, 1622, 1385, 65, 458, 4468, 66, 3941, 4, 173, 36, 256, 5, 25, 100, 43, 838, 112, 50, 670, 2, 9, 35, 480, 284, 5, 150, 4, 172, 112, 167, 2, 336, 385, 39, 4, 172, 4536, 1111, 17, 546, 38, 13, 447, 4, 192, 50, 16, 6, 147, 2025, 19, 14, 22, 4, 1920, 4613, 469, 4, 22, 71, 87, 12, 16, 43, 530, 38, 76, 15, 13, 1247, 4, 22, 17, 515, 17, 12, 16, 626, 18, 2, 5, 62, 386, 12, 8, 316, 8, 106, 5, 4, 2223, 5244, 16, 480, 66, 3785, 33, 4, 130, 12, 16, 38, 619, 5, 25, 124, 51, 36, 135, 48, 25, 1415, 33, 6, 22, 12, 215, 28, 77, 52, 5, 14, 407, 16, 82, 2, 8, 4, 107, 117, 5952, 15, 256, 4, 2, 7, 3766, 5, 723, 36, 71, 43, 530, 476, 26, 400, 317, 46, 7, 4, 2, 1029, 13, 104, 88, 4, 381, 15, 297, 98, 32, 2071, 56, 26, 141, 6, 194, 7486, 18, 4, 226, 22, 21, 134, 476, 26, 480, 5, 144, 30, 5535, 18, 51, 36, 28, 224, 92, 25, 104, 4, 226, 65, 16, 38, 1334, 88, 12, 16, 283, 5, 16, 4472, 113, 103, 32, 15, 16, 5345, 19, 178, 32]
# 영화 리뷰들은 길이가 다릅니다. 다음 코드는 첫 번째 리뷰와 두 번째 리뷰에서 단어의 개수를 출력합니다. # 신경망의 입력은 길이가 같아야 하기 때문에 나중에 이 문제를 해결하겠습니다. len(train_data[0]), len(train_data[1]) # 텍스트 분류(단어의 수, 실제로 사용자가 리뷰한 데이터임. 0번과 1번의 길이가 다르다.)
(218, 189)
#정수를 단어로 다시 변환하기 #정수를 다시 텍스트로 변환하는 방법이 있다면 유용할 것입니다. #여기에서는 정수와 문자열을 매핑한 딕셔너리(dictionary-사전) 객체에 질의하는 헬퍼(helper) 함수를 만들겠습니다: # 단어와 정수 인덱스를 매핑한 딕셔너리 word_index = imdb.get_word_index() # 함수를 이용해 단어들에 대한 인덱스 번호 추출 # 처음 몇 개 인덱스는 사전에 정의되어 있습니다 word_index = {k:(v+3) for k,v in word_index.items()} # .items : 키와 값 모두 꺼낸다. word_index["<PAD>"] = 0 word_index["<START>"] = 1 word_index["<UNK>"] = 2 # unknown word_index["<UNUSED>"] = 3 reverse_word_index = dict([(value, key) for (key, value) in word_index.items()]) # 키와 인덱스의 자리를 바꿔주는 작업 def decode_review(text): return ' '.join([reverse_word_index.get(i, '?') for i in text])
#이제 decode_review 함수를 사용해 첫 번째 리뷰 텍스트를 출력할 수 있습니다: decode_review(train_data[0])
"<START> this film was just brilliant casting location scenery story direction everyone's really suited the part they played and you could just imagine being there robert <UNK> is an amazing actor and now the same being director <UNK> father came from the same scottish island as myself so i loved the fact there was a real connection with this film the witty remarks throughout the film were great it was just brilliant so much that i bought the film as soon as it was released for <UNK> and would recommend it to everyone to watch and the fly fishing was amazing really cried at the end it was so sad and you know what they say if you cry at a film it must have been good and this definitely was also <UNK> to the two little boy's that played the <UNK> of norman and paul they were just brilliant children are often left out of the <UNK> list i think because the stars that play them all grown up are such a big profile for the whole film but these children are amazing and should be praised for what they have done don't you think the whole story was so lovely because it was true and was someone's life after all that was shared with us all"
#데이터 준비 ''' 리뷰-정수 배열-는 신경망에 주입하기 전에 텐서로 변환되어야 합니다. 변환하는 방법에는 몇 가지가 있습니다: 원-핫 인코딩(one-hot encoding)은 정수 배열을 0과 1로 이루어진 벡터로 변환합니다. 예를 들어 배열 [3, 5]을 인덱스 3과 5만 1이고 나머지는 모두 0인 10,000차원 벡터로 변환할 수 있습니다. 그다음 실수 벡터 데이터를 다룰 수 있는 층-Dense 층-을 신경망의 첫 번째 층으로 사용합니다. 이 방법은 num_words * num_reviews 크기의 행렬이 필요하기 때문에 메모리를 많이 사용합니다. 다른 방법으로는, 정수 배열의 길이가 모두 같도록 패딩(padding)을 추가해 max_length * num_reviews 크기의 정수 텐서를 만듭니다. 이런 형태의 텐서를 다룰 수 있는 임베딩(embedding) 층을 신경망의 첫 번째 층으로 사용할 수 있습니다. 이 튜토리얼에서는 두 번째 방식을 사용하겠습니다. 영화 리뷰의 길이가 같아야 하므로 pad_sequences 함수를 사용해 길이를 맞추겠습니다 ''' #길이를 맞춰주는 함수(짧으면 뒤를 전부 0으로 채워준다.) train_data = keras.preprocessing.sequence.pad_sequences(train_data, value=word_index["<PAD>"], padding='post', maxlen=256) # 최대 길이 값 test_data = keras.preprocessing.sequence.pad_sequences(test_data, value=word_index["<PAD>"], padding='post', maxlen=256)
#샘플의 길이를 확인해 보죠: len(train_data[0]), len(train_data[1]) # 길이가 같아진 것을 확인할 수 있다.
(256, 256)
#(패딩된) 첫 번째 리뷰 내용을 확인해 보죠: print(train_data[0]) # 이 숫자는 해당 단어들을 정수화 시켜놓은 것 이다. # 끝 부분의 빈 공간들은 0으로 자동으로 채워진 것을 확인할 수 있다.(패딩시퀀스 함수로 인해 채워짐)
[ 1 14 22 16 43 530 973 1622 1385 65 458 4468 66 3941 4 173 36 256 5 25 100 43 838 112 50 670 2 9 35 480 284 5 150 4 172 112 167 2 336 385 39 4 172 4536 1111 17 546 38 13 447 4 192 50 16 6 147 2025 19 14 22 4 1920 4613 469 4 22 71 87 12 16 43 530 38 76 15 13 1247 4 22 17 515 17 12 16 626 18 2 5 62 386 12 8 316 8 106 5 4 2223 5244 16 480 66 3785 33 4 130 12 16 38 619 5 25 124 51 36 135 48 25 1415 33 6 22 12 215 28 77 52 5 14 407 16 82 2 8 4 107 117 5952 15 256 4 2 7 3766 5 723 36 71 43 530 476 26 400 317 46 7 4 2 1029 13 104 88 4 381 15 297 98 32 2071 56 26 141 6 194 7486 18 4 226 22 21 134 476 26 480 5 144 30 5535 18 51 36 28 224 92 25 104 4 226 65 16 38 1334 88 12 16 283 5 16 4472 113 103 32 15 16 5345 19 178 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
#모델 구성 ''' 신경망은 층(layer)을 쌓아서 만듭니다. 이 구조에서는 두 가지를 결정해야 합니다: 모델에서 얼마나 많은 층을 사용할 것인가? 각 층에서 얼마나 많은 은닉 유닛(hidden unit)을 사용할 것인가? 이 예제의 입력 데이터는 단어 인덱스의 배열입니다. \ 예측할 레이블은 0 또는 1입니다. 이 문제에 맞는 모델을 구성해 보죠: ''' # 입력 크기는 영화 리뷰 데이터셋에 적용된 어휘 사전의 크기입니다(10,000개의 단어) vocab_size = 10000 model = keras.Sequential() #오전코드는 시퀀스안에 층을 만들었는데 이렇게 객체를 만들고 add하는것도 똑같음 model.add(keras.layers.Embedding(vocab_size, 16, input_shape=(None,))) #Embedding층 : 갖고있는데이터는 정수, 정수로 만들어진데이터를 전달받게되면 아까 정수를 텍스트로 바꾼것처럼 입력받은 정수값을 인코딩된 정수값으로 내장시킨다는 뜻 model.add(keras.layers.GlobalAveragePooling1D()) model.add(keras.layers.Dense(16, activation='relu')) model.add(keras.layers.Dense(1, activation='sigmoid')) model.summary() #만든 모델에 대한 요약
Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= embedding_1 (Embedding) (None, None, 16) 160000 _________________________________________________________________ global_average_pooling1d_1 ( (None, 16) 0 _________________________________________________________________ dense_2 (Dense) (None, 16) 272 _________________________________________________________________ dense_3 (Dense) (None, 1) 17 ================================================================= Total params: 160,289 Trainable params: 160,289 Non-trainable params: 0 _________________________________________________________________
''' 층을 순서대로 쌓아 분류기(classifier)를 만듭니다: 첫 번째 층은 Embedding 층입니다. 이 층은 정수로 인코딩된 단어를 입력 받고 각 단어 인덱스에 해당하는 임베딩 벡터를 찾습니다. 이 벡터는 모델이 훈련되면서 학습됩니다. 이 벡터는 출력 배열에 새로운 차원으로 추가됩니다. 최종 차원은 (batch, sequence, embedding)이 됩니다. 그 다음 GlobalAveragePooling1D 층은 sequence 차원에 대해 평균을 계산하여 각 샘플에 대해 고정된 길이의 출력 벡터를 반환합니다. 이는 길이가 다른 입력을 다루는 가장 간단한 방법입니다. 이 고정 길이의 출력 벡터는 16개의 은닉 유닛을 가진 완전 연결(fully-connected) 층(Dense)을 거칩니다. 마지막 층은 하나의 출력 노드(node)를 가진 완전 연결 층입니다. sigmoid 활성화 함수를 사용하여 0과 1 사이의 실수를 출력합니다. 이 값은 확률 또는 신뢰도를 나타냅니다. ''' #은닉 유닛 ''' 위 모델에는 입력과 출력 사이에 두 개의 중간 또는 "은닉" 층이 있습니다. 출력(유닛 또는 노드, 뉴런)의 개수는 층이 가진 표현 공간(representational space)의 차원이 됩니다. 다른 말로 하면, 내부 표현을 학습할 때 허용되는 네트워크 자유도의 양입니다. 모델에 많은 은닉 유닛(고차원의 표현 공간)과 층이 있다면 네트워크는 더 복잡한 표현을 학습할 수 있습니다. 하지만 네트워크의 계산 비용이 많이 들고 원치않는 패턴을 학습할 수도 있습니다. 이런 표현은 훈련 데이터의 성능을 향상시키지만 테스트 데이터에서는 그렇지 못합니다. 이를 과대적합(overfitting)이라고 부릅니다. 나중에 이에 대해 알아 보겠습니다. ''' #손실 함수와 옵티마이저 ''' 모델이 훈련하려면 손실 함수(loss function)과 옵티마이저(optimizer)가 필요합니다. 이 예제는 이진 분류 문제이고 모델이 확률을 출력하므로(출력층의 유닛이 하나이고 sigmoid 활성화 함수를 사용합니다), binary_crossentropy 손실 함수를 사용하겠습니다. 다른 손실 함수를 선택할 수 없는 것은 아닙니다. 예를 들어 mean_squared_error를 선택할 수 있습니다. 하지만 일반적으로 binary_crossentropy가 확률을 다루는데 적합합니다. 이 함수는 확률 분포 간의 거리를 측정합니다. 여기에서는 정답인 타깃 분포와 예측 분포 사이의 거리입니다. 나중에 회귀(regression) 문제(예를 들어 주택 가격을 예측하는 문제)에 대해 살펴 볼 때 평균 제곱 오차(mean squared error) 손실 함수를 어떻게 사용하는지 알아 보겠습니다. 이제 모델이 사용할 옵티마이저와 손실 함수를 설정해 보죠: ''' model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
#검증 세트 만들기 ''' 모델을 훈련할 때 모델이 만난 적 없는 데이터에서 정확도를 확인하는 것이 좋습니다. 원본 훈련 데이터에서 10,000개의 샘플을 떼어내어 검증 세트(validation set)를 만들겠습니다. (왜 테스트 세트를 사용하지 않을까요? 훈련 데이터만을 사용하여 모델을 개발하고 튜닝하는 것이 목표입니다. 그 다음 테스트 세트를 사용해서 딱 한 번만 정확도를 평가합니다). ''' x_val = train_data[:10000] partial_x_train = train_data[10000:] y_val = train_labels[:10000] partial_y_train = train_labels[10000:]
#모델 훈련 ''' 이 모델을 512개의 샘플로 이루어진 미니배치(mini-batch)에서 40번의 에포크(epoch) 동안 훈련합니다. x_train과 y_train 텐서에 있는 모든 샘플에 대해 40번 반복한다는 뜻입니다. 훈련하는 동안 10,000개의 검증 세트에서 모델의 손실과 정확도를 모니터링합니다: ''' history = model.fit(partial_x_train,# fit함수를 통해 훈련, history 변수를 만들어주었으니 history에 계속 누적되는 것임 partial_y_train, epochs=40, # 40회 훈련 batch_size=512, # 한번 훈련시킬 때 사용할 훈련데이터의 개수는 512개 validation_data=(x_val, y_val), # 검증하는 코드 verbose=1)
Epoch 1/40 30/30 [==============================] - 1s 24ms/step - loss: 0.6919 - accuracy: 0.5321 - val_loss: 0.6903 - val_accuracy: 0.5051 Epoch 2/40 30/30 [==============================] - 1s 21ms/step - loss: 0.6864 - accuracy: 0.6286 - val_loss: 0.6829 - val_accuracy: 0.6773 Epoch 3/40 30/30 [==============================] - 1s 21ms/step - loss: 0.6750 - accuracy: 0.7208 - val_loss: 0.6685 - val_accuracy: 0.7392 Epoch 4/40 30/30 [==============================] - 1s 21ms/step - loss: 0.6546 - accuracy: 0.7513 - val_loss: 0.6453 - val_accuracy: 0.7555 Epoch 5/40 30/30 [==============================] - 1s 21ms/step - loss: 0.6249 - accuracy: 0.7747 - val_loss: 0.6140 - val_accuracy: 0.7840 Epoch 6/40 30/30 [==============================] - 1s 22ms/step - loss: 0.5869 - accuracy: 0.8042 - val_loss: 0.5769 - val_accuracy: 0.7997 ... Epoch 33/40 30/30 [==============================] - 1s 21ms/step - loss: 0.1268 - accuracy: 0.9626 - val_loss: 0.2912 - val_accuracy: 0.8851 Epoch 34/40 30/30 [==============================] - 1s 22ms/step - loss: 0.1234 - accuracy: 0.9651 - val_loss: 0.2933 - val_accuracy: 0.8861 Epoch 35/40 30/30 [==============================] - 1s 23ms/step - loss: 0.1181 - accuracy: 0.9664 - val_loss: 0.2957 - val_accuracy: 0.8852 Epoch 36/40 30/30 [==============================] - 1s 22ms/step - loss: 0.1128 - accuracy: 0.9682 - val_loss: 0.2969 - val_accuracy: 0.8853 Epoch 37/40 30/30 [==============================] - 1s 21ms/step - loss: 0.1097 - accuracy: 0.9691 - val_loss: 0.2993 - val_accuracy: 0.8846 Epoch 38/40 30/30 [==============================] - 1s 21ms/step - loss: 0.1044 - accuracy: 0.9713 - val_loss: 0.3017 - val_accuracy: 0.8836 Epoch 39/40 30/30 [==============================] - 1s 22ms/step - loss: 0.1025 - accuracy: 0.9731 - val_loss: 0.3053 - val_accuracy: 0.8822 Epoch 40/40 30/30 [==============================] - 1s 22ms/step - loss: 0.0982 - accuracy: 0.9740 - val_loss: 0.3072 - val_accuracy: 0.8839
#모델 평가 #모델의 성능을 확인해 보죠. 두 개의 값이 반환됩니다. #손실(오차를 나타내는 숫자이므로 낮을수록 좋습니다)과 정확도입니다. results = model.evaluate(test_data, test_labels, verbose=2) print(results) #accuracy: 0.8724(87.2%)
782/782 - 1s - loss: 0.3278 - accuracy: 0.8724 [0.3277806043624878, 0.8723999857902527]
#이 예제는 매우 단순한 방식을 사용하므로 87% 정도의 정확도를 달성했습니다. #고급 방법을 사용한 모델은 95%에 가까운 정확도를 얻습니다.
#정확도와 손실 그래프 그리기 #model.fit()은 History 객체를 반환합니다. 여기에는 훈련하는 동안 일어난 모든 정보가 담긴 딕셔너리(dictionary)가 들어 있습니다: #( 전체 코드를 다시 실행시키면 반복학습이 된다.(처음부터 다시하는게 아님) ) history_dict = history.history # 앞쪽의 history가 우리가 만든 변수.내부에 있는 history변수 history_dict.keys()
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])
#네 개의 항목이 있습니다. 훈련과 검증 단계에서 모니터링하는 지표들입니다. #훈련 손실과 검증 손실을 그래프로 그려 보고, 훈련 정확도와 검증 정확도도 그래프로 그려서 비교해 보겠습니다: # 손실률을 나타내는 차트 import matplotlib.pyplot as plt # 시각화 import acc = history_dict['accuracy'] # 훈련 정확도 값 꺼내고 val_acc = history_dict['val_accuracy'] # 검증 정확도 값 꺼내고 loss = history_dict['loss'] # 훈련 loss 값 꺼내고 val_loss = history_dict['val_loss'] # 검증 loss 값 꺼내고 epochs = range(1, len(acc) + 1) # 범위 만들 때 acc변수나 loss 변수나 데이터 개수가 똑같으니까 아무거나 써도 상관 없다. 40까지 나오게 하기 위해 +1 해준다. # "bo"는 "파란색 점"입니다 plt.plot(epochs, loss, 'bo', label='Training loss') # b는 "파란 실선"입니다 plt.plot(epochs, val_loss, 'b', label='Validation loss') plt.title('Training and validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show()
전송중...
사진 설명을 입력하세요.
# 정확도를 나타내는 차트 plt.clf() # 그림을 초기화합니다 plt.plot(epochs, acc, 'bo', label='Training acc') plt.plot(epochs, val_acc, 'b', label='Validation acc') plt.title('Training and validation accuracy') plt.xlabel('Epochs') plt.ylabel('Accuracy') plt.legend() plt.show()
전송중...
사진 설명을 입력하세요.
# validation 사용자 관점 # verification 개발자 관점 ''' 이 그래프에서 점선은 훈련 손실과 훈련 정확도를 나타냅니다. 실선은 검증 손실과 검증 정확도입니다. 훈련 손실은 에포크마다 감소하고 훈련 정확도는 증가한다는 것을 주목하세요. 경사 하강법 최적화를 사용할 때 볼 수 있는 현상입니다. 매 반복마다 최적화 대상의 값을 최소화합니다. 하지만 검증 손실과 검증 정확도에서는 그렇지 못합니다. --------------- 약 20번째 에포크 이후가 최적점인 것 같습니다. ------------------- 이는 과대적합 때문입니다. 이전에 본 적 없는 데이터보다 훈련 데이터에서 더 잘 동작합니다. 이 지점부터는 모델이 과도하게 최적화되어 테스트 데이터에서 일반화되기 어려운 훈련 데이터의 특정 표현을 학습합니다. 여기에서는 과대적합을 막기 위해 단순히 20번째 에포크 근처에서 훈련을 멈출 수 있습니다. 나중에 콜백(callback)을 사용하여 자동으로 이렇게 하는 방법을 배워 보겠습니다. '''