감정분석 (Emotion Analysis) 연구를 위해 구글 Colaboratory 환경에서 VAE 모델을 학습시키고 예측하는 샘플 파이썬 코드를 작성하였습니다.
1. 라이브러리 설치 및 구글 드라이브 마운트
# 필요한 라이브러리를 설치합니다. !pip install tensorflow # Google Drive를 마운트합니다. from google.colab import drive drive.mount('/content/drive') import os os.environ["KERAS_BACKEND"] = "tensorflow" import numpy as np import pandas as pd import tensorflow as tf from tensorflow import keras from tensorflow.keras import layers from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences
2. Colab에 학습시킬 데이터 업로드
- 학습 데이터는 문장별로 17개의 감정중 1가지가 라벨링 되어 있습니다.
- 테스트 데이터셋 출처 : https://github.com/searle-j/KOTE
# 파일 경로 설정 file_path = '/content/drive/My Drive/emotion/emotion_dataset_korean.csv' # 데이터 불러오기 (csv 파일에서 sentence와 label 컬럼이 있다고 가정합니다) df = pd.read_csv(file_path, encoding='cp949') sentences = df['sentence'].tolist() labels = df['label'].tolist() # 라벨 값 검토 print("Unique labels:", set(labels))
3. VAE 모델 구축
- 학습 데이터에 따라 아래 3가지 하이퍼파라미터 설정이 필요합니다.
- 잠재 공간 차원의 최적 갯수는 확인이 필요한데, 선행연구에서 제시한 8개로 우선 설정했습니다.
- 학습 시, 코렙 환경이 중도 종료될 경우를 대비하여 체크포인트 옵션을 추가했습니다.
# 하이퍼파라미터 설정 max_len = 40 # 최대 문장 길이, 단어 vocab_size = 10000 # 단어 집합 크기 embedding_dim = 16 # 임베딩 차원 # 토크나이저를 사용해 텍스트를 시퀀스로 변환 tokenizer = Tokenizer(num_words=vocab_size) tokenizer.fit_on_texts(sentences) sequences = tokenizer.texts_to_sequences(sentences) padded_sequences = pad_sequences(sequences, maxlen=max_len, padding='post') # 데이터셋 준비 data = np.array(padded_sequences) class Sampling(layers.Layer): """Uses (z_mean, z_log_var) to sample z, the vector encoding a sentence.""" def __init__(self, **kwargs): super().__init__(**kwargs) self.seed = 1337 def call(self, inputs): z_mean, z_log_var = inputs batch = tf.shape(z_mean)[0] dim = tf.shape(z_mean)[1] epsilon = tf.random.normal(shape=(batch, dim), seed=self.seed) return z_mean + tf.exp(0.5 * z_log_var) * epsilon # 하이퍼파라미터 latent_dim = 8 # 잠재 공간의 차원 # 인코더 구축 encoder_inputs = keras.Input(shape=(max_len,)) x = layers.Embedding(input_dim=vocab_size, output_dim=embedding_dim, input_length=max_len)(encoder_inputs) x = layers.LSTM(16, return_sequences=False)(x) z_mean = layers.Dense(latent_dim, name="z_mean")(x) z_log_var = layers.Dense(latent_dim, name="z_log_var")(x) z = Sampling()([z_mean, z_log_var]) encoder = keras.Model(encoder_inputs, [z_mean, z_log_var, z], name="encoder") encoder.summary() # 디코더 구축 latent_inputs = keras.Input(shape=(latent_dim,)) x = layers.Dense(max_len * embedding_dim, activation="relu")(latent_inputs) x = layers.Reshape((max_len, embedding_dim))(x) decoder_outputs = layers.LSTM(embedding_dim, return_sequences=True)(x) decoder_outputs = layers.TimeDistributed(layers.Dense(vocab_size, activation="softmax"))(decoder_outputs) decoder = keras.Model(latent_inputs, decoder_outputs, name="decoder") decoder.summary() # VAE 모델 정의 class VAE(keras.Model): def __init__(self, encoder, decoder, **kwargs): super().__init__(**kwargs) self.encoder = encoder self.decoder = decoder def train_step(self, data): if isinstance(data, tuple): data = data[0] with tf.GradientTape() as tape: z_mean, z_log_var, z = self.encoder(data) reconstruction = self.decoder(z) reconstruction_loss = tf.reduce_mean( tf.reduce_sum( keras.losses.sparse_categorical_crossentropy(data, reconstruction), axis=1 ) ) kl_loss = -0.5 * tf.reduce_mean( z_log_var - tf.square(z_mean) - tf.exp(z_log_var) + 1 ) total_loss = reconstruction_loss + kl_loss grads = tape.gradient(total_loss, self.trainable_weights) self.optimizer.apply_gradients(zip(grads, self.trainable_weights)) return {"loss": total_loss, "reconstruction_loss": reconstruction_loss, "kl_loss": kl_loss} # VAE 모델 인스턴스화 및 컴파일 vae = VAE(encoder, decoder) vae.compile(optimizer=keras.optimizers.Adam()) # 저장된 가중치 파일 로드 # 마지막에 저장된 가중치 파일의 경로를 제공해야 합니다. latest_weights_filepath = '/content/drive/My Drive/emotion/checkpoints/vae_weights_05.weights.h5' vae.load_weights(latest_weights_filepath) # ModelCheckpoint 콜백 설정 checkpoint_filepath = '/content/drive/My Drive/emotion/checkpoints/vae_weights_{epoch:02d}.weights.h5' checkpoint_callback = keras.callbacks.ModelCheckpoint( filepath=checkpoint_filepath, save_weights_only=True, save_freq='epoch' ) # 추가 학습 vae.fit(data, data, epochs=30, batch_size=2, callbacks=[checkpoint_callback]) # 최종 가중치 저장 (옵션) vae.save_weights('/content/drive/My Drive/emotion/vae_model_final.weights.h5')
4. 필요 시, 가중치를 구글 클라우드에 저장
# 가중치 저장 vae.save_weights('/content/drive/My Drive/emotion/vae_model_5times_weights.h5') # 가중치를 불러올 때 # 모델을 다시 정의한 후 #vae.load_weights('/content/drive/My Drive/emotion/vae_model_5times_weights.h5')
5. 노래 가사 감정 예측
- 노래 가사 문장별로 8개 잠재공간 차원별 가중치를 예측합니다.
- KPOP 노래 가사 파일
# 파일 경로 설정 file_path = '/content/drive/My Drive/emotion/kpop_lyrics.csv' # CSV 파일 읽기 df = pd.read_csv(file_path, encoding='cp949') new_sentences = df['lyric'].tolist() new_sequences = tokenizer.texts_to_sequences(new_sentences) new_padded_sequences = pad_sequences(new_sequences, maxlen=max_len, padding='post') z_mean, z_log_var, z = encoder.predict(new_padded_sequences) # 잠재 벡터를 데이터프레임에 추가 for i in range(latent_dim): df[f'z_{i}'] = z[:, i] # 수정된 데이터프레임을 구글 드라이브에 저장 output_file_path = '/content/drive/My Drive/emotion/kpop_lyrics_with_z.csv' df.to_csv(output_file_path, index=False, encoding='cp949') print(f"Data with latent vectors saved to {output_file_path}") # 디코더를 사용해 재구성된 문장 생성 #reconstructed_sentences = decoder.predict(z) #print("Reconstructed sentences:", reconstructed_sentences)
6. 예측 결과
- 가사 별로 8가지 감정으로 예측한 결과를 확인할 수 있습니다.

댓글