공유자료 HOME > 자료실 > 공유자료
 
[PYTHON] LMDB 파이썬
관리자 19-10-23 15:33 123

import os


imdb_dir = 'D:/src/aclImdb'
train_dir = os.path.join(imdb_dir, 'train')  # aclimdb 폴더의 훈련 데이터 내용을 가져온다


labels = []    # labels와 texts 라는 두 개의 빈 리스트를 만든다
texts = []


for label_type in ['neg', 'pos']:  # train 폴더에 있는 pos 12,500 + neg 12,500개 데이터 읽는다
     dir_name = os.path.join(train_dir, label_type) # neg와 pos 폴더 각각에 접근한다


for fname in os.listdir(dir_name):
  if fname[-4:] == '.txt':  # 마지막 4 글자가 .txt 로 끝나는지를 확인한다
       f = open(os.path.join(dir_name, fname), encoding='utf8')
       texts.append(f.read()) # 텍스트를 읽어서 texts 리스트에 연결한다
       f.close()  if label_type == 'neg':  # 만약 현재 폴더가 neg 폴더라면
    labels.append(0) # texts와 같은 순서의 labels 리스트에는 0을 저장한다
  else:
    labels.append(1) # pos 폴더라면 같은 순서의 labels 리스트에 1을 저장한다


# 데이터 확인
print('texts 0:', texts[0])
print('texts len:', len(texts))


print('labels 0:', labels[0])
print('labels len:', len(labels))


# 텍스트에 사용된 단어의 종류를 빈도 순으로 정렬하는 작업을 수행한다
from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences
import numpy as np
import math


validation_ratio = math.floor(len(texts) * 0.3)   # 검증 샘플은 전체의 30%로 한다
max_words = 10000                  # 데이터셋에서 가장 빈도 높은 10,000 개의 단어만 사용한다
maxlen = 200     # 항상 200 단어가 되도록 길이를 고정한다


tokenizer = Tokenizer(num_words=max_words) # 상위빈도 10,000 개의 단어만을 추려내는 Tokenizer 객체 생성
tokenizer.fit_on_texts(texts)        # 단어 인덱스를 구축한다
word_index = tokenizer.word_index        # 단어 인덱스만 가져온다


# Tokenizing 결과 확인
print('전체에서 %s개의 고유한 토큰을 찾았습니다.' % len(word_index))
print('word_index type: ', type(word_index))
print('word_index: ', word_index)


# 문자를 숫자로 변환하는 작업을 수행한다
# 상위 빈도 10,000(max_words)개의 단어만 추출하여 word_index의 숫자 리스트로 변환한다.
data = tokenizer.texts_to_sequences(texts)  # Tokenizer 결과가 여기서 반영된다.


print('data 0:', data[0])


# Padding은 데이터의 길이를 고정시켜 준다
# 지정된 길이에 모자라는 것은 0으로 채우고, 넘치는 것은 잘라낸다
# 텐서의 크기를 맞춰야 하는 경우 유용하다
# one-hot encoding 등을 통해 길이가 고정될 수 있다면 하지 않아도 된다
# 단어의 선택은 뒤에서부터 하며, nested list를 2D 텐서(2차원 넘파이 배열)로 만든다
from keras.preprocessing.sequence import pad_sequences


sequences = [[1, 2, 3, 4, 5], [1, 2, 3, 4], [1]]    # nested list
padded = pad_sequences(sequences, maxlen=3)    # 2D tensor
print(padded)


data = pad_sequences(data, maxlen=maxlen)


print('data:', data)
print('data 0:', data[0])


# one-hot encoding은 모든 숫자를 0과 1로만 만든다
# 원-핫 인코딩 함수
def to_one_hot(sequences, dimension):
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.
    return results


# data를 one-hot-인코딩으로 0과 1의 벡터로 변환
# label은 이미 0과 1로 태깅되어 있으므로, list에서 넘파이 배열로만 변환. float32를 지정하지 않으면 int32로 저장된다
data = to_one_hot(data, dimension=max_words)
labels = np.asarray(labels).astype('float32')


print('data:', data)


len(data[0])     # dimension=10000으로 했으므로 각 행은 10,000개를 가지고 있다
print('data [0][0:100]:', data[0][0:100])


## Train 데이터와 Validation 데이터 준비
print('데이터 텐서의 크기:', data.shape)    # (25000, 10000)
print('레이블 텐서의 크기:', labels.shape)   # (25000,) data와 label이 모두 2D 텐서가 되었음


indices = np.arange(data.shape[0])   # 0 ~ 24999 까지의 숫자를 생성
np.random.shuffle(indices)        # 0 ~ 24999 까지의 숫자를 랜덤하게 섞음
data = data[indices]        # 이것을 인덱스로 하여 2D 텐서 데이터를 섞음
labels = labels[indices]    # label도 같은 순서로 섞음


x_train = data[validation_ratio:]    # 훈련데이터의 70%를 훈련데이터
y_train = labels[validation_ratio:]   # 훈련데이터의 70%를 훈련데이터 Label (data와 labels는 같은 순서)
x_val = data[:validation_ratio]    # 훈련데이터의 30%를 검증데이터
y_val = labels[:validation_ratio]    # 훈련데이터의 30%를 검증데이터 Label


## 모델 정의하기
from keras.models import Sequential
from keras.layers import Dense


model = Sequential()                                                          # 모델을 새로 정의


model.add(Dense(64, activation='relu', input_shape=(max_words,))) # 첫 번째 은닉층
model.add(Dense(32, activation='relu'))                             # 두 번째 은닉층
model.add(Dense(1, activation='sigmoid'))                   # 출력층


model.summary()


# 모델 컴파일
# 가중치 업데이트 방법은 RMSprop을 사용하였다. 이동평균의 방법을 도입하여 조절해간다
# 신경망의 출력이 확률이므로 crossentropy를 사용하는 것이 최선이다
# crossentropy는 원본의 확률 분포와 예측의 확률 분포를 측정하여 조절해 간다
# 또한 이진 분류이므로 binary_crossentropy를 사용한다
model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['acc'])


# 모델 훈련
# 32개씩 미니 배치를 만들어 10번의 epoch로 훈련한다. 보통 32개에서 시작하여 512개까지 중에서 찾는다
# 훈련 데이터로 훈련하고, 검증 데이터로 검증한다
# 반환값의 history는 훈련하는 동안 발생한 모든 정보를 담고 있는 딕셔너리이다
history = model.fit(x_train, y_train, epochs=10, batch_size=32, validation_data=(x_val, y_val))
history_dict = history.history


# multidimensional numpy arrays를 저장할 수 있는 h5 file(HDF) 포맷으로 저장한다
model.save('text_binary_model.h5')


# 훈련데이터에서 사용된 상위빈도 10,000개의 단어로 된 Tokenizer 저장
# 새로 입력되는 문장에서도 같은 단어가 추출되게 한다
import pickle


with open('text_binary_tokenizer', 'wb') as handle:
       pickle.dump(tokenizer, handle, protocol=pickle.HIGHEST_PROTOCOL)


# history 딕셔너리 안에 있는 정확도와 손실값을 가져와 본다
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']


print('Accuracy of each epoch:', acc)  # [0.79, 0.90, 0.93, 0.94, 0.96, 0.97, 0.98, 0.98, 0.98, 0.99]
epochs = range(1, len(acc) +1)   # range(1, 11)


import matplotlib.pyplot as plt


# 훈련데이터의 정확도에 비해 검증데이터의 정확도는 낮게 나타난다
# epoch가 높아지면 모델은 훈련데이터에 매우 민감해져 오히려 새로운 데이터를 잘 못 맞춘다
plt.plot(epochs, acc, 'bo', label='Training Acc')
plt.plot(epochs, val_acc, 'b', label='Validation Acc')
plt.title('Training and validation accuracy')
plt.legend()


plt.figure()    # 새로운 그림을 그린다

# 훈련데이터의 손실값은 낮아지나, 검증데이터의 손실값은 높아진다
# 손실값은 오류값을 말한다. 예측과 정답의 차이를 거리 계산으로 구한 값이다
plt.plot(epochs, loss, 'bo', label='Training Loss')
plt.plot(epochs, val_loss, 'b', label='Validation Loss')
plt.title('Training and validation loss')
plt.legend()


plt.show()