# 처리해야 할 문장을 파이썬 리스트에 옮겨 담았습니다.
sentences=['i feel hungry', 'i eat lunch', 'now i feel happy']
# 파이썬 split() 메소드를 이용해 단어 단위로 문장을 쪼개 봅니다.
word_list = 'i feel hungry'.split()
print(word_list)
['i', 'feel', 'hungry']
index_to_word={} # 빈 딕셔너리를 만들어서
# 단어들을 하나씩 채워 봅니다. 채우는 순서는 일단 임의로 하였습니다. 그러나 사실 순서는 중요하지 않습니다.
# <BOS>, <PAD>, <UNK>는 관례적으로 딕셔너리 맨 앞에 넣어줍니다.
index_to_word[0]='<PAD>' # 패딩용 단어
index_to_word[1]='<BOS>' # 문장의 시작지점
index_to_word[2]='<UNK>' # 사전에 없는(Unknown) 단어
index_to_word[3]='i'
index_to_word[4]='feel'
index_to_word[5]='hungry'
index_to_word[6]='eat'
index_to_word[7]='lunch'
index_to_word[8]='now'
index_to_word[9]='happy'
print(index_to_word)
{0: '<PAD>', 1: '<BOS>', 2: '<UNK>', 3: 'i', 4: 'feel', 5: 'hungry', 6: 'eat', 7: 'lunch', 8: 'now', 9: 'happy'}
텍스트 데이터를 숫자로 바꿔 보려고 하는데, 텍스트를 숫자로 바꾸려면 위의 딕셔너리가 {텍스트:인덱스} 구조
word_to_index={word:index for index, word in index_to_word.items()}
print(word_to_index)
{'<PAD>': 0, '<BOS>': 1, '<UNK>': 2, 'i': 3, 'feel': 4, 'hungry': 5, 'eat': 6, 'lunch': 7, 'now': 8, 'happy': 9}
print(word_to_index['i']) # 단어 'feel'은 숫자 인덱스 4로 바뀝니다.
3
-인코딩
# 문장 1개를 활용할 딕셔너리와 함께 주면, 단어 인덱스 리스트로 변환해 주는 함수를 만들어 봅시다.
# 단, 모든 문장은 <BOS>로 시작하는 것으로 합니다.
def get_encoded_sentence(sentence, word_to_index):
return [word_to_index['<BOS>']]+[word_to_index[word] if word in word_to_index else word_to_index['<UNK>'] for word in sentence.split()]
print(get_encoded_sentence('i eat lunch', word_to_index))
[1, 3, 6, 7]
# 여러 개의 문장 리스트를 한꺼번에 숫자 텐서로 encode해 주는 함수입니다.
def get_encoded_sentences(sentences, word_to_index):
return [get_encoded_sentence(sentence, word_to_index) for sentence in sentences]
# sentences=['i feel hungry', 'i eat lunch', 'now i feel happy'] 가 아래와 같이 변환됩니다.
encoded_sentences = get_encoded_sentences(sentences, word_to_index)
print(encoded_sentences)
[[1, 3, 4, 5], [1, 3, 6, 7], [1, 8, 3, 4, 9]]
-디코딩
# 숫자 벡터로 encode된 문장을 원래대로 decode하는 함수입니다.
def get_decoded_sentence(encoded_sentence, index_to_word):
return ' '.join(index_to_word[index] if index in index_to_word else '<UNK>' for index in encoded_sentence[1:]) #[1:]를 통해 <BOS>를 제외
print(get_decoded_sentence([1, 3, 4, 5], index_to_word))
i feel hungry
# 여러 개의 숫자 벡터로 encode된 문장을 한꺼번에 원래대로 decode하는 함수입니다.
def get_decoded_sentences(encoded_sentences, index_to_word):
return [get_decoded_sentence(encoded_sentence, index_to_word) for encoded_sentence in encoded_sentences]
# encoded_sentences=[[1, 3, 4, 5], [1, 3, 6, 7], [1, 8, 3, 4, 9]] 가 아래와 같이 변환됩니다.
print(get_decoded_sentences(encoded_sentences, index_to_word))
['i feel hungry', 'i eat lunch', 'now i feel happy']
2. Embedding 레이어의 등장¶
# 아래 코드는 그대로 실행하시면 에러가 발생할 것입니다.
import numpy as np
import tensorflow as tf
from tensorflow import keras
vocab_size = len(word_to_index) # 위 예시에서 딕셔너리에 포함된 단어 개수는 10
word_vector_dim = 4 # 위 그림과 같이 4차원의 워드 벡터를 가정합니다.
embedding = tf.keras.layers.Embedding(input_dim=vocab_size, output_dim=word_vector_dim, mask_zero=True)
# 숫자로 변환된 텍스트 데이터 [[1, 3, 4, 5], [1, 3, 6, 7], [1, 8, 3, 4, 9]] 에 Embedding 레이어를 적용합니다.
raw_inputs = np.array(get_encoded_sentences(sentences, word_to_index))
output = embedding(raw_inputs)
print(output)
/opt/conda/lib/python3.7/site-packages/ipykernel_launcher.py:13: VisibleDeprecationWarning: Creating an ndarray from ragged nested sequences (which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes) is deprecated. If you meant to do this, you must specify 'dtype=object' when creating the ndarray
del sys.path[0]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-63-190c1be7d3f8> in <module>
12 # 숫자로 변환된 텍스트 데이터 [[1, 3, 4, 5], [1, 3, 6, 7], [1, 8, 3, 4, 9]] 에 Embedding 레이어를 적용합니다.
13 raw_inputs = np.array(get_encoded_sentences(sentences, word_to_index))
---> 14 output = embedding(raw_inputs)
15 print(output)
/opt/conda/lib/python3.7/site-packages/tensorflow/python/keras/engine/base_layer.py in __call__(self, *args, **kwargs)
958 if any(isinstance(x, (
959 np_arrays.ndarray, np.ndarray, float, int)) for x in input_list):
--> 960 inputs = nest.map_structure(_convert_numpy_or_python_types, inputs)
961 input_list = nest.flatten(inputs)
962
/opt/conda/lib/python3.7/site-packages/tensorflow/python/util/nest.py in map_structure(func, *structure, **kwargs)
657
658 return pack_sequence_as(
--> 659 structure[0], [func(*x) for x in entries],
660 expand_composites=expand_composites)
661
/opt/conda/lib/python3.7/site-packages/tensorflow/python/util/nest.py in <listcomp>(.0)
657
658 return pack_sequence_as(
--> 659 structure[0], [func(*x) for x in entries],
660 expand_composites=expand_composites)
661
/opt/conda/lib/python3.7/site-packages/tensorflow/python/keras/engine/base_layer.py in _convert_numpy_or_python_types(x)
3307 def _convert_numpy_or_python_types(x):
3308 if isinstance(x, (np_arrays.ndarray, np.ndarray, float, int)):
-> 3309 return ops.convert_to_tensor_v2_with_dispatch(x)
3310 return x
3311
/opt/conda/lib/python3.7/site-packages/tensorflow/python/util/dispatch.py in wrapper(*args, **kwargs)
199 """Call target, and fall back on dispatchers if there is a TypeError."""
200 try:
--> 201 return target(*args, **kwargs)
202 except (TypeError, ValueError):
203 # Note: convert_to_eager_tensor currently raises a ValueError, not a
/opt/conda/lib/python3.7/site-packages/tensorflow/python/framework/ops.py in convert_to_tensor_v2_with_dispatch(value, dtype, dtype_hint, name)
1403 """
1404 return convert_to_tensor_v2(
-> 1405 value, dtype=dtype, dtype_hint=dtype_hint, name=name)
1406
1407
/opt/conda/lib/python3.7/site-packages/tensorflow/python/framework/ops.py in convert_to_tensor_v2(value, dtype, dtype_hint, name)
1413 name=name,
1414 preferred_dtype=dtype_hint,
-> 1415 as_ref=False)
1416
1417
/opt/conda/lib/python3.7/site-packages/tensorflow/python/profiler/trace.py in wrapped(*args, **kwargs)
161 with Trace(trace_name, **trace_kwargs):
162 return func(*args, **kwargs)
--> 163 return func(*args, **kwargs)
164
165 return wrapped
/opt/conda/lib/python3.7/site-packages/tensorflow/python/framework/ops.py in convert_to_tensor(value, dtype, name, as_ref, preferred_dtype, dtype_hint, ctx, accepted_result_types)
1538
1539 if ret is None:
-> 1540 ret = conversion_func(value, dtype=dtype, name=name, as_ref=as_ref)
1541
1542 if ret is NotImplemented:
/opt/conda/lib/python3.7/site-packages/tensorflow/python/framework/tensor_conversion_registry.py in _default_conversion_function(***failed resolving arguments***)
50 def _default_conversion_function(value, dtype, name, as_ref):
51 del as_ref # Unused.
---> 52 return constant_op.constant(value, dtype, name=name)
53
54
/opt/conda/lib/python3.7/site-packages/tensorflow/python/framework/constant_op.py in constant(value, dtype, shape, name)
263 """
264 return _constant_impl(value, dtype, shape, name, verify_shape=False,
--> 265 allow_broadcast=True)
266
267
/opt/conda/lib/python3.7/site-packages/tensorflow/python/framework/constant_op.py in _constant_impl(value, dtype, shape, name, verify_shape, allow_broadcast)
274 with trace.Trace("tf.constant"):
275 return _constant_eager_impl(ctx, value, dtype, shape, verify_shape)
--> 276 return _constant_eager_impl(ctx, value, dtype, shape, verify_shape)
277
278 g = ops.get_default_graph()
/opt/conda/lib/python3.7/site-packages/tensorflow/python/framework/constant_op.py in _constant_eager_impl(ctx, value, dtype, shape, verify_shape)
299 def _constant_eager_impl(ctx, value, dtype, shape, verify_shape):
300 """Implementation of eager constant."""
--> 301 t = convert_to_eager_tensor(value, ctx, dtype)
302 if shape is None:
303 return t
/opt/conda/lib/python3.7/site-packages/tensorflow/python/framework/constant_op.py in convert_to_eager_tensor(value, ctx, dtype)
96 dtype = dtypes.as_dtype(dtype).as_datatype_enum
97 ctx.ensure_initialized()
---> 98 return ops.EagerTensor(value, ctx.device_name, dtype)
99
100
ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type list).
raw_inputs = keras.preprocessing.sequence.pad_sequences(raw_inputs,
value=word_to_index['<PAD>'],
padding='post',
maxlen=5)
print(raw_inputs)
import numpy as np
import tensorflow as tf
vocab_size = len(word_to_index) # 위 예시에서 딕셔너리에 포함된 단어 개수는 10
word_vector_dim = 4 # 그림과 같이 4차원의 워드 벡터를 가정합니다.
embedding = tf.keras.layers.Embedding(input_dim=vocab_size, output_dim=word_vector_dim, mask_zero=True)
# keras.preprocessing.sequence.pad_sequences를 통해 word vector를 모두 일정 길이로 맞춰주어야
# embedding 레이어의 input이 될 수 있음에 주의해 주세요.
raw_inputs = np.array(get_encoded_sentences(sentences, word_to_index))
# 함수를 통해 문장 벡터 뒤에 패딩(<PAD>)을 추가하여 길이를 일정하게 맞춰주는 기능을 제공
raw_inputs = keras.preprocessing.sequence.pad_sequences(raw_inputs,
value=word_to_index['<PAD>'],
padding='post',
maxlen=5)
output = embedding(raw_inputs)
#output 3,5,4에서 3은 입력문장 개수, 5는 입력문장의 최대 길이, 4는 워드 벡터의 차원 수
print("\n\n", output)
3. 시퀀스 데이터를 다루는 RNN¶
vocab_size = 10 # 어휘 사전의 크기입니다(10개의 단어)
word_vector_dim = 4 # 단어 하나를 표현하는 임베딩 벡터의 차원수입니다.
model = keras.Sequential()
#Embedding()에 넣어야하는 대표적인 인자
#첫번째 인자 = 단어 집합의 크기. 즉, 총 단어의 개수
#두번째 인자 = 임베딩 벡터의 출력 차원. 결과로서 나오는 임베딩 벡터의 크기
#input_length = 입력 시퀀스의 길이
model.add(keras.layers.Embedding(vocab_size, word_vector_dim, input_shape=(None,)))
model.add(keras.layers.LSTM(8)) # 가장 널리 쓰이는 RNN인 LSTM 레이어를 사용하였습니다. 이때 LSTM state 벡터의 차원수는 8로 하였습니다. (변경 가능)
model.add(keras.layers.Dense(8, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid')) # 최종 출력은 긍정/부정을 나타내는 1dim 입니다.
model.summary()
- 텍스트를 처리하기 위해 RNN이 아니라 1-D Convolution Neural Network(1-D CNN)를 사용
vocab_size = 10 # 어휘 사전의 크기입니다(10개의 단어)
word_vector_dim = 4 # 단어 하나를 표현하는 임베딩 벡터의 차원 수입니다.
model = keras.Sequential()
model.add(keras.layers.Embedding(vocab_size, word_vector_dim, input_shape=(None,)))
model.add(keras.layers.Conv1D(16, 7, activation='relu'))
model.add(keras.layers.MaxPooling1D(5))
model.add(keras.layers.Conv1D(16, 7, activation='relu'))
model.add(keras.layers.GlobalMaxPooling1D())
model.add(keras.layers.Dense(8, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid')) # 최종 출력은 긍정/부정을 나타내는 1dim 입니다.
model.summary()
GlobalMaxPooling1D() 레이어 하나만 사용하는 방법도 생각,
전체 문장 중에서 단 하나의 가장 중요한 단어만 피처로 추출하여 그것으로 문장의 긍정/부정을 평가하는 방식
vocab_size = 10 # 어휘 사전의 크기입니다(10개의 단어)
word_vector_dim = 4 # 단어 하나를 표현하는 임베딩 벡터의 차원 수입니다.
model = keras.Sequential()
model.add(keras.layers.Embedding(vocab_size, word_vector_dim, input_shape=(None,)))
model.add(keras.layers.GlobalMaxPooling1D())
model.add(keras.layers.Dense(8, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid')) # 최종 출력은 긍정/부정을 나타내는 1dim 입니다.
model.summary()
Model: "sequential_4"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_10 (Embedding) (None, None, 4) 40
_________________________________________________________________
global_max_pooling1d_2 (Glob (None, 4) 0
_________________________________________________________________
dense_8 (Dense) (None, 8) 40
_________________________________________________________________
dense_9 (Dense) (None, 1) 9
=================================================================
Total params: 89
Trainable params: 89
Non-trainable params: 0
_________________________________________________________________
import tensorflow as tf
from tensorflow import keras
import numpy as np
print(tf.__version__)
imdb = keras.datasets.imdb
# IMDb 데이터셋 다운로드
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=10000)
print("훈련 샘플 개수: {}, 테스트 개수: {}".format(len(x_train), len(x_test)))
2.4.1
훈련 샘플 개수: 25000, 테스트 개수: 25000
print(x_train[0]) # 1번째 리뷰데이터
#텍스트 데이터가 아니라 이미 숫자로 encode된 텍스트 데이터를 다운로드받았음을 확인
print('라벨: ', y_train[0]) # 1번째 리뷰데이터의 라벨
print('1번째 리뷰 문장 길이: ', len(x_train[0]))
print('2번째 리뷰 문장 길이: ', len(x_train[1]))
[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]
라벨: 1
1번째 리뷰 문장 길이: 218
2번째 리뷰 문장 길이: 189
word_to_index = imdb.get_word_index()
index_to_word = {index:word for word, index in word_to_index.items()}
print(index_to_word[1]) # 'the' 가 출력됩니다.
print(word_to_index['the']) # 1 이 출력됩니다.
the
1
#실제 인코딩 인덱스는 제공된 word_to_index에서 index 기준으로 3씩 뒤로 밀려 있습니다.
word_to_index = {k:(v+3) for k,v in word_to_index.items()}
# 처음 몇 개 인덱스는 사전에 정의되어 있습니다
word_to_index["<PAD>"] = 0
word_to_index["<BOS>"] = 1
word_to_index["<UNK>"] = 2 # unknown
word_to_index["<UNUSED>"] = 3
index_to_word[0] = "<PAD>"
index_to_word[1] = "<BOS>"
index_to_word[2] = "<UNK>"
index_to_word[3] = "<UNUSED>"
index_to_word = {index:word for word, index in word_to_index.items()}
print(index_to_word[1]) # '<BOS>' 가 출력됩니다.
print(word_to_index['the']) # 4 이 출력됩니다.
print(index_to_word[4]) # 'the' 가 출력됩니다.
<BOS>
4
the
print(get_decoded_sentence(x_train[0], index_to_word))
print('라벨: ', y_train[0]) # 1번째 리뷰데이터의 라벨
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
라벨: 1
pad_sequences를 통해 데이터셋 상의 문장의 길이를 통일
문장 최대 길이 maxlen의 값 설정도 전체 모델 성능에 영향
이 길이도 적절한 값을 찾기 위해서는 전체 데이터셋의 분포를 확인
#data 는 인코딩된 형태임 즉 정수형
total_data_text = list(x_train) + list(x_test)
# 텍스트데이터 문장길이의 리스트를 생성한 후
num_tokens = [len(tokens) for tokens in total_data_text]
num_tokens = np.array(num_tokens)
# 문장길이의 평균값, 최대값, 표준편차를 계산해 본다.
print('문장길이 평균 : ', np.mean(num_tokens))
print('문장길이 최대 : ', np.max(num_tokens))
print('문장길이 표준편차 : ', np.std(num_tokens))
# 예를들어, 최대 길이를 (평균 + 2*표준편차)로 한다면,
max_tokens = np.mean(num_tokens) + 2 * np.std(num_tokens)
maxlen = int(max_tokens)
print('pad_sequences maxlen : ', maxlen)
print('전체 문장의 {}%가 maxlen 설정값 이내에 포함됩니다. '.format(np.sum(num_tokens < max_tokens) / len(num_tokens)))
문장길이 평균 : 234.75892
문장길이 최대 : 2494
문장길이 표준편차 : 172.91149458735703
pad_sequences maxlen : 580
전체 문장의 0.94536%가 maxlen 설정값 이내에 포함됩니다.
padding 방식을 문장 뒤쪽('post')과 앞쪽('pre') 중 어느쪽으로 하느냐에 따라 RNN을 이용한 딥러닝 적용 시 성능 차이가 발생
마지막 입력이 최종 state 값에 가장 영향을 많이 미치게 됩니다. 그러므로 마지막 입력이 무의미한 padding으로 채워지는 것은 비효율적, 'pre'가 훨씬 유리
x_train = keras.preprocessing.sequence.pad_sequences(x_train,
value=word_to_index["<PAD>"],
padding='post', # 혹은 'pre'
maxlen=maxlen)
x_test = keras.preprocessing.sequence.pad_sequences(x_test,
value=word_to_index["<PAD>"],
padding='post', # 혹은 'pre'
maxlen=maxlen)
print("post", x_train.shape)
x_train = keras.preprocessing.sequence.pad_sequences(x_train,
value=word_to_index["<PAD>"],
padding='pre', # 혹은 'pre'
maxlen=maxlen)
x_test = keras.preprocessing.sequence.pad_sequences(x_test,
value=word_to_index["<PAD>"],
padding='pre', # 혹은 'pre'
maxlen=maxlen)
print("pre", x_train.shape)
post (25000, 580)
pre (25000, 580)
2. 딥러닝 모델 설계와 훈련¶
vocab_size = 10000 # 어휘 사전의 크기입니다(10,000개의 단어)
word_vector_dim = 16 # 워드 벡터의 차원 수 (변경 가능한 하이퍼파라미터)
# model 설계 - 딥러닝 모델 코드를 직접 작성해 주세요.
model = keras.Sequential()
# [[YOUR CODE]]
#Embedding()에 넣어야하는 대표적인 인자
#첫번째 인자 = 단어 집합의 크기. 즉, 총 단어의 개수
#두번째 인자 = 임베딩 벡터의 출력 차원. 결과로서 나오는 임베딩 벡터의 크기
#input_length = 입력 시퀀스의 길이
model.add(keras.layers.Embedding(vocab_size, word_vector_dim, input_shape=(None,)))
model.add(keras.layers.LSTM(8)) # 가장 널리 쓰이는 RNN인 LSTM 레이어를 사용하였습니다. 이때 LSTM state 벡터의 차원수는 8로 하였습니다. (변경 가능)
model.add(keras.layers.Dense(8, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid')) # 최종 출력은 긍정/부정을 나타내는 1dim 입니다.
model.summary()
Model: "sequential_7"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_11 (Embedding) (None, None, 16) 160000
_________________________________________________________________
lstm_2 (LSTM) (None, 8) 800
_________________________________________________________________
dense_10 (Dense) (None, 8) 72
_________________________________________________________________
dense_11 (Dense) (None, 1) 9
=================================================================
Total params: 160,881
Trainable params: 160,881
Non-trainable params: 0
_________________________________________________________________
- model 훈련 전에, 훈련용 데이터셋 25000건 중 10000건을 분리하여 검증셋(validation set)으로 사용
# validation set 10000건 분리
x_val = x_train[:10000]
y_val = y_train[:10000]
# validation set을 제외한 나머지 15000건
partial_x_train = x_train[10000:]
partial_y_train = y_train[10000:]
print(partial_x_train.shape)
print(partial_y_train.shape)
(15000, 580)
(15000,)
- model 학습을 시작
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
epochs=20 # 몇 epoch를 훈련하면 좋을지 결과를 보면서 바꾸어 봅시다.
#model.fit() 과정 중의 train/validation loss, accuracy 등이 매 epoch마다 history 변수에 저장
#이 데이터 그래프 가능
history = model.fit(partial_x_train,
partial_y_train,
epochs=epochs,
batch_size=512,
validation_data=(x_val, y_val),
verbose=1)
Epoch 1/20
30/30 [==============================] - 6s 108ms/step - loss: 0.6932 - accuracy: 0.5018 - val_loss: 0.6932 - val_accuracy: 0.5012
Epoch 2/20
30/30 [==============================] - 3s 90ms/step - loss: 0.6929 - accuracy: 0.5094 - val_loss: 0.6929 - val_accuracy: 0.5010
Epoch 3/20
30/30 [==============================] - 3s 91ms/step - loss: 0.6919 - accuracy: 0.5129 - val_loss: 0.6920 - val_accuracy: 0.5053
Epoch 4/20
30/30 [==============================] - 3s 91ms/step - loss: 0.6881 - accuracy: 0.5059 - val_loss: 0.6894 - val_accuracy: 0.5088
Epoch 5/20
30/30 [==============================] - 3s 91ms/step - loss: 0.6865 - accuracy: 0.5260 - val_loss: 0.6892 - val_accuracy: 0.5088
Epoch 6/20
30/30 [==============================] - 3s 90ms/step - loss: 0.6815 - accuracy: 0.5268 - val_loss: 0.6894 - val_accuracy: 0.5058
Epoch 7/20
30/30 [==============================] - 3s 91ms/step - loss: 0.6775 - accuracy: 0.5241 - val_loss: 0.6894 - val_accuracy: 0.5068
Epoch 8/20
30/30 [==============================] - 3s 90ms/step - loss: 0.6759 - accuracy: 0.5257 - val_loss: 0.6898 - val_accuracy: 0.5083
Epoch 9/20
30/30 [==============================] - 3s 91ms/step - loss: 0.6729 - accuracy: 0.5303 - val_loss: 0.6910 - val_accuracy: 0.5094
Epoch 10/20
30/30 [==============================] - 3s 91ms/step - loss: 0.6698 - accuracy: 0.5370 - val_loss: 0.6918 - val_accuracy: 0.5109
Epoch 11/20
30/30 [==============================] - 3s 92ms/step - loss: 0.6693 - accuracy: 0.5342 - val_loss: 0.6901 - val_accuracy: 0.5139
Epoch 12/20
30/30 [==============================] - 3s 92ms/step - loss: 0.6681 - accuracy: 0.5324 - val_loss: 0.6924 - val_accuracy: 0.5166
Epoch 13/20
30/30 [==============================] - 3s 93ms/step - loss: 0.6661 - accuracy: 0.5408 - val_loss: 0.6586 - val_accuracy: 0.7065
Epoch 14/20
30/30 [==============================] - 3s 92ms/step - loss: 0.6572 - accuracy: 0.6233 - val_loss: 0.7987 - val_accuracy: 0.7056
Epoch 15/20
30/30 [==============================] - 3s 90ms/step - loss: 0.6810 - accuracy: 0.5865 - val_loss: 0.6908 - val_accuracy: 0.5144
Epoch 16/20
30/30 [==============================] - 3s 92ms/step - loss: 0.6746 - accuracy: 0.5412 - val_loss: 0.6887 - val_accuracy: 0.5157
Epoch 17/20
30/30 [==============================] - 3s 90ms/step - loss: 0.6701 - accuracy: 0.5374 - val_loss: 0.6873 - val_accuracy: 0.5173
Epoch 18/20
30/30 [==============================] - 3s 91ms/step - loss: 0.6685 - accuracy: 0.5448 - val_loss: 0.6890 - val_accuracy: 0.5263
Epoch 19/20
30/30 [==============================] - 3s 91ms/step - loss: 0.6387 - accuracy: 0.6419 - val_loss: 0.5999 - val_accuracy: 0.6886
Epoch 20/20
30/30 [==============================] - 3s 91ms/step - loss: 0.6937 - accuracy: 0.6358 - val_loss: 0.9247 - val_accuracy: 0.5058
- 모델 테스트셋으로 평가
results = model.evaluate(x_test, y_test, verbose=2)
print(results)
782/782 - 17s - loss: 0.9322 - accuracy: 0.5007
[0.9322213530540466, 0.5007200241088867]
history 변수 이 데이터를 그래프로 그려 보면, 수행했던 딥러닝 학습이 잘 진행되었는지, 오버피팅 혹은 언더피팅하지 않았는지, 성능을 개선할 수 있는 다양한 아이디어를 얻을 수 있는 좋은 자료
history_dict = history.history
print(history_dict.keys()) # epoch에 따른 그래프를 그려볼 수 있는 항목들
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])
import matplotlib.pyplot as plt
acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']
epochs = range(1, len(acc) + 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()
3. Word2Vec의 적용¶
감성분류 모델이 학습이 잘 되었다면, Embedding 레이어에 학습된 우리의 워드 벡터들도 의미 공간상에 유의미한 형태로 학습되었을 것입니다. 한번 확인
embedding_layer = model.layers[0]
weights = embedding_layer.get_weights()[0]
print(weights.shape) # shape: (vocab_size, embedding_dim)
(10000, 16)
import os
# 학습한 Embedding 파라미터를 파일에 써서 저장합니다.
word2vec_file_path = os.getenv('HOME')+'/aiffel/sentiment_classification/data/word2vec.txt'
f = open(word2vec_file_path, 'w')
f.write('{} {}\n'.format(vocab_size-4, word_vector_dim)) # 몇개의 벡터를 얼마 사이즈로 기재할지 타이틀을 씁니다.
# 단어 개수(에서 특수문자 4개는 제외하고)만큼의 워드 벡터를 파일에 기록합니다.
vectors = model.get_weights()[0]
for i in range(4,vocab_size):
f.write('{} {}\n'.format(index_to_word[i], ' '.join(map(str, list(vectors[i, :])))))
f.close()
gensim에서 제공하는 패키지를 이용해, 위에 남긴 임베딩 파라미터를 읽어서 word vector로 활용
from gensim.models.keyedvectors import Word2VecKeyedVectors
word_vectors = Word2VecKeyedVectors.load_word2vec_format(word2vec_file_path, binary=False)
vector = word_vectors['computer']
vector
array([ 0.08587243, -0.01414933, 0.03725335, -0.09449606, -0.07416076,
0.0247446 , -0.0477853 , 0.00273205, 0.01142045, 0.09010214,
0.00019948, -0.00384799, -0.01445462, 0.00046435, -0.06917786,
-0.01763034], dtype=float32)
워드 벡터가 의미벡터 공간상에 유의미하게 학습되었는지 확인하는 방법 중
단어를 하나 주고 그와 가장 유사한 단어와 그 유사도를 확인하는 방법
word_vectors.similar_by_word("love")
[('holding', 0.9360864162445068),
('everybody', 0.9289427399635315),
('appeal', 0.9279565811157227),
('route', 0.9137934446334839),
('rides', 0.9085736274719238),
('realm', 0.9072042107582092),
('chain', 0.9047648310661316),
('bar', 0.9044411182403564),
('mclaglen', 0.9041990041732788),
('celine', 0.9035897254943848)]
- 구글에서 제공하는 Word2Vec이라는 사전학습된(Pretrained) 워드 임베딩 모델을 가져다 활용
from gensim.models import KeyedVectors
word2vec_path = os.getenv('HOME')+'/aiffel/sentiment_classification/data/GoogleNews-vectors-negative300.bin.gz'
# KeyedVectors.load_word2vec_format 메소드로 워드 벡터를 로딩할 때 가장 많이 사용되는 상위 100만 개만 limt으로 조건을 주어 로딩
# 메모리가 충분하다면 limt=None으로 하시면 300만 개를 모두 로딩
word2vec = KeyedVectors.load_word2vec_format(word2vec_path, binary=True, limit=1000000)
vector = word2vec['computer']
vector # 무려 300dim의 워드 벡터입니다.
array([ 1.07421875e-01, -2.01171875e-01, 1.23046875e-01, 2.11914062e-01,
-9.13085938e-02, 2.16796875e-01, -1.31835938e-01, 8.30078125e-02,
2.02148438e-01, 4.78515625e-02, 3.66210938e-02, -2.45361328e-02,
2.39257812e-02, -1.60156250e-01, -2.61230469e-02, 9.71679688e-02,
-6.34765625e-02, 1.84570312e-01, 1.70898438e-01, -1.63085938e-01,
-1.09375000e-01, 1.49414062e-01, -4.65393066e-04, 9.61914062e-02,
1.68945312e-01, 2.60925293e-03, 8.93554688e-02, 6.49414062e-02,
3.56445312e-02, -6.93359375e-02, -1.46484375e-01, -1.21093750e-01,
-2.27539062e-01, 2.45361328e-02, -1.24511719e-01, -3.18359375e-01,
-2.20703125e-01, 1.30859375e-01, 3.66210938e-02, -3.63769531e-02,
-1.13281250e-01, 1.95312500e-01, 9.76562500e-02, 1.26953125e-01,
6.59179688e-02, 6.93359375e-02, 1.02539062e-02, 1.75781250e-01,
-1.68945312e-01, 1.21307373e-03, -2.98828125e-01, -1.15234375e-01,
5.66406250e-02, -1.77734375e-01, -2.08984375e-01, 1.76757812e-01,
2.38037109e-02, -2.57812500e-01, -4.46777344e-02, 1.88476562e-01,
5.51757812e-02, 5.02929688e-02, -1.06933594e-01, 1.89453125e-01,
-1.16210938e-01, 8.49609375e-02, -1.71875000e-01, 2.45117188e-01,
-1.73828125e-01, -8.30078125e-03, 4.56542969e-02, -1.61132812e-02,
1.86523438e-01, -6.05468750e-02, -4.17480469e-02, 1.82617188e-01,
2.20703125e-01, -1.22558594e-01, -2.55126953e-02, -3.08593750e-01,
9.13085938e-02, 1.60156250e-01, 1.70898438e-01, 1.19628906e-01,
7.08007812e-02, -2.64892578e-02, -3.08837891e-02, 4.06250000e-01,
-1.01562500e-01, 5.71289062e-02, -7.26318359e-03, -9.17968750e-02,
-1.50390625e-01, -2.55859375e-01, 2.16796875e-01, -3.63769531e-02,
2.24609375e-01, 8.00781250e-02, 1.56250000e-01, 5.27343750e-02,
1.50390625e-01, -1.14746094e-01, -8.64257812e-02, 1.19140625e-01,
-7.17773438e-02, 2.73437500e-01, -1.64062500e-01, 7.29370117e-03,
4.21875000e-01, -1.12792969e-01, -1.35742188e-01, -1.31835938e-01,
-1.37695312e-01, -7.66601562e-02, 6.25000000e-02, 4.98046875e-02,
-1.91406250e-01, -6.03027344e-02, 2.27539062e-01, 5.88378906e-02,
-3.24218750e-01, 5.41992188e-02, -1.35742188e-01, 8.17871094e-03,
-5.24902344e-02, -1.74713135e-03, -9.81445312e-02, -2.86865234e-02,
3.61328125e-02, 2.15820312e-01, 5.98144531e-02, -3.08593750e-01,
-2.27539062e-01, 2.61718750e-01, 9.86328125e-02, -5.07812500e-02,
1.78222656e-02, 1.31835938e-01, -5.35156250e-01, -1.81640625e-01,
1.38671875e-01, -3.10546875e-01, -9.71679688e-02, 1.31835938e-01,
-1.16210938e-01, 7.03125000e-02, 2.85156250e-01, 3.51562500e-02,
-1.01562500e-01, -3.75976562e-02, 1.41601562e-01, 1.42578125e-01,
-5.68847656e-02, 2.65625000e-01, -2.09960938e-01, 9.64355469e-03,
-6.68945312e-02, -4.83398438e-02, -6.10351562e-02, 2.45117188e-01,
-9.66796875e-02, 1.78222656e-02, -1.27929688e-01, -4.78515625e-02,
-7.26318359e-03, 1.79687500e-01, 2.78320312e-02, -2.10937500e-01,
-1.43554688e-01, -1.27929688e-01, 1.73339844e-02, -3.60107422e-03,
-2.04101562e-01, 3.63159180e-03, -1.19628906e-01, -6.15234375e-02,
5.93261719e-02, -3.23486328e-03, -1.70898438e-01, -3.14941406e-02,
-8.88671875e-02, -2.89062500e-01, 3.44238281e-02, -1.87500000e-01,
2.94921875e-01, 1.58203125e-01, -1.19628906e-01, 7.61718750e-02,
6.39648438e-02, -4.68750000e-02, -6.83593750e-02, 1.21459961e-02,
-1.44531250e-01, 4.54101562e-02, 3.68652344e-02, 3.88671875e-01,
1.45507812e-01, -2.55859375e-01, -4.46777344e-02, -1.33789062e-01,
-1.38671875e-01, 6.59179688e-02, 1.37695312e-01, 1.14746094e-01,
2.03125000e-01, -4.78515625e-02, 1.80664062e-02, -8.54492188e-02,
-2.48046875e-01, -3.39843750e-01, -2.83203125e-02, 1.05468750e-01,
-2.14843750e-01, -8.74023438e-02, 7.12890625e-02, 1.87500000e-01,
-1.12304688e-01, 2.73437500e-01, -3.26171875e-01, -1.77734375e-01,
-4.24804688e-02, -2.69531250e-01, 6.64062500e-02, -6.88476562e-02,
-1.99218750e-01, -7.03125000e-02, -2.43164062e-01, -3.66210938e-02,
-7.37304688e-02, -1.77734375e-01, 9.17968750e-02, -1.25000000e-01,
-1.65039062e-01, -3.57421875e-01, -2.85156250e-01, -1.66992188e-01,
1.97265625e-01, -1.53320312e-01, 2.31933594e-02, 2.06054688e-01,
1.80664062e-01, -2.74658203e-02, -1.92382812e-01, -9.61914062e-02,
-1.06811523e-02, -4.73632812e-02, 6.54296875e-02, -1.25732422e-02,
1.78222656e-02, -8.00781250e-02, -2.59765625e-01, 9.37500000e-02,
-7.81250000e-02, 4.68750000e-02, -2.22167969e-02, 1.86767578e-02,
3.11279297e-02, 1.04980469e-02, -1.69921875e-01, 2.58789062e-02,
-3.41796875e-02, -1.44042969e-02, -5.46875000e-02, -8.78906250e-02,
1.96838379e-03, 2.23632812e-01, -1.36718750e-01, 1.75781250e-01,
-1.63085938e-01, 1.87500000e-01, 3.44238281e-02, -5.63964844e-02,
-2.27689743e-05, 4.27246094e-02, 5.81054688e-02, -1.07910156e-01,
-3.88183594e-02, -2.69531250e-01, 3.34472656e-02, 9.81445312e-02,
5.63964844e-02, 2.23632812e-01, -5.49316406e-02, 1.46484375e-01,
5.93261719e-02, -2.19726562e-01, 6.39648438e-02, 1.66015625e-02,
4.56542969e-02, 3.26171875e-01, -3.80859375e-01, 1.70898438e-01,
5.66406250e-02, -1.04492188e-01, 1.38671875e-01, -1.57226562e-01,
3.23486328e-03, -4.80957031e-02, -2.48046875e-01, -6.20117188e-02],
dtype=float32)
# 메모리를 다소 많이 소비하는 작업이니 유의해 주세요.
word2vec.similar_by_word("love")
[('loved', 0.6907791495323181),
('adore', 0.6816873550415039),
('loves', 0.661863386631012),
('passion', 0.6100708842277527),
('hate', 0.600395679473877),
('loving', 0.5886635780334473),
('affection', 0.5664337873458862),
('undying_love', 0.5547304749488831),
('absolutely_adore', 0.5536840558052063),
('adores', 0.5440906882286072)]
- 이전 스텝에서 학습했던 모델의 임베딩 레이어를 Word2Vec의 것으로 교체하여 다시 학습
vocab_size = 10000 # 어휘 사전의 크기입니다(10,000개의 단어)
word_vector_dim = 300 # 워드 벡터의 차원수
embedding_matrix = np.random.rand(vocab_size, word_vector_dim)
# embedding_matrix에 Word2Vec 워드 벡터를 단어 하나씩마다 차례차례 카피한다.
for i in range(4,vocab_size):
if index_to_word[i] in word2vec:
embedding_matrix[i] = word2vec[index_to_word[i]]
from tensorflow.keras.initializers import Constant
vocab_size = 10000 # 어휘 사전의 크기입니다(10,000개의 단어)
word_vector_dim = 300 # 워드 벡터의 차원 수
# 모델 구성
model = keras.Sequential()
model.add(keras.layers.Embedding(vocab_size,
word_vector_dim,
embeddings_initializer=Constant(embedding_matrix), # 카피한 임베딩을 여기서 활용
input_length=maxlen,
trainable=True)) # trainable을 True로 주면 Fine-tuning
model.add(keras.layers.Conv1D(16, 7, activation='relu'))
model.add(keras.layers.MaxPooling1D(5))
model.add(keras.layers.Conv1D(16, 7, activation='relu'))
model.add(keras.layers.GlobalMaxPooling1D())
model.add(keras.layers.Dense(8, activation='relu'))
model.add(keras.layers.Dense(1, activation='sigmoid'))
model.summary()
Model: "sequential_8"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding_12 (Embedding) (None, 580, 300) 3000000
_________________________________________________________________
conv1d_2 (Conv1D) (None, 574, 16) 33616
_________________________________________________________________
max_pooling1d_1 (MaxPooling1 (None, 114, 16) 0
_________________________________________________________________
conv1d_3 (Conv1D) (None, 108, 16) 1808
_________________________________________________________________
global_max_pooling1d_3 (Glob (None, 16) 0
_________________________________________________________________
dense_12 (Dense) (None, 8) 136
_________________________________________________________________
dense_13 (Dense) (None, 1) 9
=================================================================
Total params: 3,035,569
Trainable params: 3,035,569
Non-trainable params: 0
_________________________________________________________________
# 학습의 진행
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
epochs=20 # 몇 epoch를 훈련하면 좋을지 결과를 보면서 바꾸어 봅시다.
history = model.fit(partial_x_train,
partial_y_train,
epochs=epochs,
batch_size=512,
validation_data=(x_val, y_val),
verbose=1)
Epoch 1/20
30/30 [==============================] - 11s 359ms/step - loss: 0.1093 - accuracy: 0.9543 - val_loss: 0.5705 - val_accuracy: 0.8666
Epoch 2/20
30/30 [==============================] - 11s 353ms/step - loss: 0.0044 - accuracy: 1.0000 - val_loss: 0.5160 - val_accuracy: 0.8705
Epoch 3/20
30/30 [==============================] - 10s 351ms/step - loss: 0.0021 - accuracy: 1.0000 - val_loss: 0.5187 - val_accuracy: 0.8708
Epoch 4/20
30/30 [==============================] - 10s 351ms/step - loss: 0.0017 - accuracy: 1.0000 - val_loss: 0.5237 - val_accuracy: 0.8717
Epoch 5/20
30/30 [==============================] - 10s 341ms/step - loss: 0.0015 - accuracy: 1.0000 - val_loss: 0.5516 - val_accuracy: 0.8718
Epoch 6/20
30/30 [==============================] - 9s 319ms/step - loss: 9.2701e-04 - accuracy: 1.0000 - val_loss: 0.5752 - val_accuracy: 0.8713
Epoch 7/20
30/30 [==============================] - 10s 320ms/step - loss: 7.2567e-04 - accuracy: 1.0000 - val_loss: 0.5903 - val_accuracy: 0.8712
Epoch 8/20
30/30 [==============================] - 10s 319ms/step - loss: 5.5165e-04 - accuracy: 1.0000 - val_loss: 0.6039 - val_accuracy: 0.8710
Epoch 9/20
30/30 [==============================] - 10s 321ms/step - loss: 4.9147e-04 - accuracy: 1.0000 - val_loss: 0.6146 - val_accuracy: 0.8705
Epoch 10/20
30/30 [==============================] - 9s 317ms/step - loss: 4.1009e-04 - accuracy: 1.0000 - val_loss: 0.6262 - val_accuracy: 0.8702
Epoch 11/20
30/30 [==============================] - 9s 318ms/step - loss: 3.3855e-04 - accuracy: 1.0000 - val_loss: 0.6379 - val_accuracy: 0.8704
Epoch 12/20
30/30 [==============================] - 9s 316ms/step - loss: 2.8127e-04 - accuracy: 1.0000 - val_loss: 0.6474 - val_accuracy: 0.8706
Epoch 13/20
30/30 [==============================] - 10s 320ms/step - loss: 2.2999e-04 - accuracy: 1.0000 - val_loss: 0.6572 - val_accuracy: 0.8704
Epoch 14/20
30/30 [==============================] - 10s 320ms/step - loss: 2.2872e-04 - accuracy: 1.0000 - val_loss: 0.6674 - val_accuracy: 0.8708
Epoch 15/20
30/30 [==============================] - 9s 317ms/step - loss: 1.8744e-04 - accuracy: 1.0000 - val_loss: 0.6762 - val_accuracy: 0.8704
Epoch 16/20
30/30 [==============================] - 10s 319ms/step - loss: 1.6585e-04 - accuracy: 1.0000 - val_loss: 0.6849 - val_accuracy: 0.8706
Epoch 17/20
30/30 [==============================] - 10s 320ms/step - loss: 1.4708e-04 - accuracy: 1.0000 - val_loss: 0.6931 - val_accuracy: 0.8704
Epoch 18/20
30/30 [==============================] - 9s 317ms/step - loss: 1.4352e-04 - accuracy: 1.0000 - val_loss: 0.7017 - val_accuracy: 0.8706
Epoch 19/20
30/30 [==============================] - 10s 320ms/step - loss: 1.2368e-04 - accuracy: 1.0000 - val_loss: 0.7096 - val_accuracy: 0.8701
Epoch 20/20
30/30 [==============================] - 9s 318ms/step - loss: 1.0979e-04 - accuracy: 1.0000 - val_loss: 0.7161 - val_accuracy: 0.8703
# 테스트셋을 통한 모델 평가
results = model.evaluate(x_test, y_test, verbose=2)
print(results)
782/782 - 6s - loss: 0.7788 - accuracy: 0.8627
[0.7787870168685913, 0.8627200126647949]
history_dict = history.history
print(history_dict.keys()) # epoch에 따른 그래프를 그려볼 수 있는 항목들
import matplotlib.pyplot as plt
acc = history_dict['accuracy']
val_acc = history_dict['val_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']
epochs = range(1, len(acc) + 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()
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])
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()
'AI > AI 실습' 카테고리의 다른 글
2차 해커톤(반값 소송) (0) | 2021.12.03 |
---|---|
1차 해커톤 : 코로나 데이터 셋 (0) | 2021.12.03 |
모의 캐글 경진대회 [EXP_6] (0) | 2021.10.12 |