지난 포스팅에서는
1. 데이터를 바탕으로 각 발화문에 새로운 감정 label 달기
2. 데이터 분포 확인
3. 학습에 이용할 1000개의 데이터 추출하기
위 세가지 작업을 수행했습니다.
이번 포스팅에서는 텍스트, 음성 데이터로부터 feature vector를 추출하는 작업을 해보겠습니다.
이 부분은 참고문헌 [1]의 kaggle notebook을 참고하였습니다.
다음 함수들을 이용하여 data augmentation을 수행할 수 있습니다.
def noise(data):
noise_amp = 0.035*np.random.uniform()*np.amax(data)
data = data + noise_amp*np.random.normal(size=data.shape[0])
return data
def stretch(data, rate=0.8):
return librosa.effects.time_stretch(data, rate)
def shift(data):
shift_range = int(np.random.uniform(low=-5, high = 5)*1000)
return np.roll(data, shift_range)
def pitch(data, sampling_rate, pitch_factor=0.7):
return librosa.effects.pitch_shift(data, sampling_rate, pitch_factor)
다음 함수는 음성 데이터로부터 feature vector를 만드는 함수입니다. 크게 다섯가지의 spectral 특성을 가지고 feature vector를 만듭니다.
(각 특성들이 어떤 의미를 가지는지는 추후 포스팅에서 살펴보겠습니다!)
def extract_features(data, sample_rate):
# ZCR
result = np.array([])
zcr = np.mean(librosa.feature.zero_crossing_rate(y=data).T, axis=0)
result=np.hstack((result, zcr)) # stacking horizontally
# Chroma_stft
stft = np.abs(librosa.stft(data))
chroma_stft = np.mean(librosa.feature.chroma_stft(S=stft, sr=sample_rate).T, axis=0)
result = np.hstack((result, chroma_stft)) # stacking horizontally
# MFCC
mfcc = np.mean(librosa.feature.mfcc(y=data, sr=sample_rate).T, axis=0)
result = np.hstack((result, mfcc)) # stacking horizontally
# Root Mean Square Value
rms = np.mean(librosa.feature.rms(y=data).T, axis=0)
result = np.hstack((result, rms)) # stacking horizontally
# MelSpectogram
mel = np.mean(librosa.feature.melspectrogram(y=data, sr=sample_rate).T, axis=0)
result = np.hstack((result, mel)) # stacking horizontally
return result
다음은 data augmentation을 수행하는 함수들과 extract_feature 함수를 이용하여 각 음성 데이터의 feature vector를 만드는 함수입니다.
def get_features(path):
data, sample_rate = librosa.load(path, duration=2.5, offset=0.0)
# without augmentation
res1 = extract_features(data, sample_rate)
result = np.array(res1)
# data with noise
noise_data = noise(data)
res2 = extract_features(noise_data, sample_rate)
result = np.concatenate((result, res2), axis = 0)
# data with stretching and pitching
new_data = stretch(data)
data_stretch_pitch = pitch(new_data, sample_rate)
res3 = extract_features(data_stretch_pitch, sample_rate)
result = np.concatenate((result, res3), axis = 0)
return result
이제, 위 함수들을 이용하여 음성 데이터 파일로부터 feature vector를 만들어보겠습니다.
X_audio, Y = [], []
for path, label in zip(wav_df['wav_id'], wav_df['final_label']):
audio_features = get_features(audio_path+'/'+path+'.wav')
X_audio.append(audio_features)
Y.append(label)
audio_features = pd.DataFrame(X_audio)
final_df = pd.concat([audio_features, wav_df[['wav_id', 'final_label', 'sentence']]], axis = 1)
final_df.head(3)
final_df는 음성 데이터로 만든 feature vector와 wav_id, final_label, 발화문(sentence)를 포함하는 데이터 프레임입니다.
우선, embedding vector 생성 및 이후 모델 훈련에 필요한 라이브러리들을 import 하겠습니다.
from sentence_transformers import SentenceTransformer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, StandardScaler, OneHotEncoder
import keras
from keras.callbacks import ReduceLROnPlateau
from keras.models import Sequential
from keras.layers import Dense, Conv1D, MaxPooling1D, Flatten, Dropout, BatchNormalization
from keras.utils import np_utils, to_categorical
from keras.callbacks import ModelCheckpoint
여러 pre-trained 모델을 이용하여 텍스트 embedding vector를 만들어보려고 합니다.
이를 위해서 다음 class를 생성하겠습니다.
class text_embedding():
def __init__(self, model_name):
self.model_name = model_name
def fit(self, X, y=None):
return self
def transform(self, X):
embedding_model = SentenceTransformer(self.model_name)
embedding_vec = embedding_model.encode(X['sentence'])
X_val = np.concatenate((X.drop(['final_label', 'wav_id', 'sentence'], axis = 1), embedding_vec), axis = 1)
return X_val
아래 모델은 [1]의 kaggle notebook을 참고하였습니다.
def custom_model(x_train):
model=Sequential()
model.add(Conv1D(256, kernel_size=5, strides=1, padding='same', activation='relu', input_shape=(x_train.shape[1], 1)))
model.add(MaxPooling1D(pool_size=5, strides = 2, padding = 'same'))
model.add(Conv1D(256, kernel_size=5, strides=1, padding='same', activation='relu'))
model.add(MaxPooling1D(pool_size=5, strides = 2, padding = 'same'))
model.add(Conv1D(128, kernel_size=5, strides=1, padding='same', activation='relu'))
model.add(MaxPooling1D(pool_size=5, strides = 2, padding = 'same'))
model.add(Dropout(0.2))
model.add(Conv1D(64, kernel_size=5, strides=1, padding='same', activation='relu'))
model.add(MaxPooling1D(pool_size=5, strides = 2, padding = 'same'))
model.add(Flatten())
model.add(Dense(units=32, activation='relu'))
model.add(Dropout(0.3))
model.add(Dense(units=6, activation='softmax'))
model.compile(optimizer = 'adam' , loss = 'categorical_crossentropy' , metrics = ['accuracy'])
#model.summary()
return model
rlrp = ReduceLROnPlateau(monitor='loss', factor=0.4, verbose=0, patience=2, min_lr=0.0000001) #learning rate 조절
pre_trained_models에 있는 여러 사전 훈련 모델을 이용하여 text embedding vector를 만들고 이를 미리 만들어둔 음성 데이터 feature vector와 합쳐서 훈련을 시켜보겠습니다.
scaler = StandardScaler()
encoder = OneHotEncoder()
pre_trained_models = ['sentence-transformers/xlm-r-100langs-bert-base-nli-stsb-mean-tokens',
'sentence-transformers/multi-qa-distilbert-cos-v1',
'jhgan/ko-sroberta-multitask',
'all-distilroberta-v1',
'jhgan/ko-sbert-multitask',
'all-MiniLM-L12-v2', 'jhgan/ko-sroberta-sts']
Y = final_df['final_label'].values
Y = encoder.fit_transform(np.array(Y).reshape(-1,1)).toarray()
for i in pre_trained_models:
txt_embed = text_embedding(model_name = i)
X = txt_embed.transform(final_df)
x_train, x_test, y_train, y_test = train_test_split(X, Y, random_state=0, shuffle=True)
x_train = scaler.fit_transform(x_train)
x_test = scaler.transform(x_test)
x_train = np.expand_dims(x_train, axis=2)
x_test = np.expand_dims(x_test, axis=2)
x_train.shape, y_train.shape, x_test.shape, y_test.shape
model = custom_model(x_train)
history=model.fit(x_train, y_train, batch_size=64, epochs=50, validation_data=(x_test, y_test), callbacks=[rlrp])
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
print("Pre-trained Model: ", i)
print("Test Accuracy: ",test_acc)
테스트 결과는 다음과 같습니다.
확실히 한국어 사전 학습 모델에 대해서 성능이 좋게 나왔습니다.
이제 음성 데이터의 특성을 더 자세히 살펴보고 모델을 바꿔가면서 테스트를 해보겠습니다 :-)
참고문헌
[1] https://www.kaggle.com/code/shivamburnwal/speech-emotion-recognition
[2] https://librosa.org/doc/main/feature.html
[3] https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ReduceLROnPlateau.html
안녕하세요. 모델을 따라 해보고 있는데 TypeError: time_stretch() takes 1 positional argument but 2 were given 이렇게 오류가 뜨는데 해결방법이 무엇인지 알 수 있을까요?