-
[BERT] BERT에 대해 쉽게 알아보기3 - tf-hub BERT layer를 이용한 캐글 분류문제✨ AI/NLP 2020. 3. 27. 17:41
● 텐서플로우 허브의 BERT layer를 이용한 캐글 분류문제 예제
지난번 포스팅에서 사전학습한 BERT 모델을 다른 NLP task 문제에 전이학습시켜 예제에 적용하는 포스팅을 이번 포스팅에서 작성하려고 하였는데요. 그 이전에 텐서플로우 허브, 허깅페이스 등을 이용해서 multilingual BERT 레이어를 실제로 예제에 어떻게 적용하는지 알아보는 포스팅을 먼저 쓰도록 하겠습니다.
● 텐서플로우 허브(TensorFlow Hub)
텐서플로우 허브는 일반화된 문제들에 대해서 모델의 재사용성을 극대화 하기 위해 구글에서 새로 공개한 API입니다. 텐서플로우 1.7.0 버전 이상에서 사용할 수 있고, pip install을 통해 설치해주어야 합니다. 텐서플로우를 이용하면 지금 사용하려는 사전 훈련된 버트 모델처럼 미리 훈련된 모델을 내가 풀고자하는 문제에 파인튜닝(Fine-tuning)하여 쉽게 사용할 수 있습니다.
pip install tensorflow-hub
파이토치를 사용하는 유저들은 주로 허깅페이스(Hugging Face) 버트모델을 많이 이용하는 것 같습니다. 버트모델, 트랜스포머 모델 등 다양한 사전훈련된 모델을 텐서플로우, 파이토치용으로 받아 사용할 수 있습니다.
● 캐글 아마존 pet product 리뷰 분류문제
약 열달 전쯤 나왔던 문제이고 텍스트 분류 문제 입니다. 리뷰에 관한 내용이 dogs에 관한 것인지, cats에 관한 것인지 그 외 (fish, birds 등)인지 분류하는 문제 입니다..!데이터에는 id, text, label 3개의 컬럼이 있고 text에는 리뷰내용, label에는 dogs, cats, birds, fish aquatic pets 등 총 6개의 라벨로 나뉘어 집니다. 트레인 데이터에는 dog에 관한 데이터가 54%, cats에 관한 데이터가 36% 그외 동물이 11%인 Imbalanced 데이터 분류 문제로 보입니다.
필요한 훈련데이터
저는 코랩 GPU환경에서 실습을 진행하였습니다.
1. 구글 공식 BERT코드와 필요한 패키지 임포트
1234!wget -q https://raw.githubusercontent.com/google-research/bert/master/modeling.py!wget -q https://raw.githubusercontent.com/google-research/bert/master/optimization.py!wget -q https://raw.githubusercontent.com/google-research/bert/master/run_classifier.py!wget -q https://raw.githubusercontent.com/google-research/bert/master/tokenization.pycs 123456789101112131415161718192021# 필요한 패키지 임포트import osimport numpy as npimport pandas as pdimport datetimeimport sysimport zipfileimport modelingimport optimizationimport run_classifierimport tokenizationfrom tokenization import FullTokenizerfrom sklearn.preprocessing import LabelEncoderimport tensorflow as tffrom sklearn.model_selection import train_test_splitimport tensorflow_hub as hubfrom tqdm import tqdm_notebookfrom tensorflow.keras import backend as Kfrom tensorflow.keras.models import Modelcs 12345# BERT모델과 tokenization의 파라미터 지정sess = tf.Session()bert_path = "https://tfhub.dev/google/bert_uncased_L-12_H-768_A-12/1"max_seq_length = 128cs
2. 데이터 로드 및 정제
구글 드라이브에 데이터 파일을 업로드 한 후, 내 구글 드라이브와 코랩 실습 파일은 연동하였습니다.
from google.coalb import drive
drive.mount('content/drive')1234# 파일 로드train_df = pd.read_csv('/content/drive/My Drive/Colab Notebooks/Kaggle/amazon_pet_product_reviews_classfication/train.csv', index_col='id')val_df = pd.read_csv('/content/drive/My Drive/Colab Notebooks/Kaggle/amazon_pet_product_reviews_classfication/valid.csv', index_col='id')test_df = pd.read_csv('/content/drive/My Drive/Colab Notebooks/Kaggle/amazon_pet_product_reviews_classfication/test.csv', index_col='id')cs 1234567891011121314151617181920212223label_encoder = LabelEncoder().fit(pd.concat([train_df['label'], val_df['label']]))X_train_val, X_test = pd.concat([train_df['text'], val_df['text']]).values, test_df['text'].valuesy_train_val = label_encoder.fit_transform(pd.concat([train_df['label'], val_df['label']]))X_train, X_val, y_train, y_val = train_test_split(X_train_val,y_train_val, test_size=0.1, random_state=0, stratify = y_train_val)train_text = X_traintrain_text = [' '.join(t.split()[0:max_seq_length]) for t in train_text]train_text = np.array(train_text, dtype=object)[:, np.newaxis]train_label = y_trainval_text = X_valval_text = [' '.join(t.split()[0:max_seq_length]) for t in val_text]val_text = np.array(val_text, dtype=object)[:, np.newaxis]val_label = y_valtest_text = X_testtest_text = [' '.join(t.split()[0:max_seq_length]) for t in test_text]test_text = np.array(test_text, dtype=object)[:, np.newaxis]cs
3. TensorFlow Hub 의 BERT layer 사용하기
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687import tensorflow as tfimport tensorflow_hub as hubimport osimport reimport numpy as npfrom tqdm import tqdm_notebookfrom keras import backend as Kfrom keras.layers import Layerclass BertLayer(Layer):def __init__(self, n_fine_tune_layers=10, tf_hub = None, output_representation = 'pooled_output', trainable = False, **kwargs):self.n_fine_tune_layers = n_fine_tune_layersself.is_trainble = trainableself.output_size = 768self.tf_hub = tf_hubself.output_representation = output_representationself.supports_masking = Truesuper(BertLayer, self).__init__(**kwargs)def build(self, input_shape):self.bert = hub.Module(self.tf_hub,trainable=self.is_trainble,name="{}_module".format(self.name))variables = list(self.bert.variable_map.values())if self.is_trainble:# 1 first remove unused layerstrainable_vars = [var for var in variables if not "/cls/" in var.name]if self.output_representation == "sequence_output" or self.output_representation == "mean_pooling":# 1 first remove unused pooled layerstrainable_vars = [var for var in trainable_vars if not "/pooler/" in var.name]# Select how many layers to fine tunetrainable_vars = trainable_vars[-self.n_fine_tune_layers :]# Add to trainable weightsfor var in trainable_vars:self._trainable_weights.append(var)# Add non-trainable weightsfor var in self.bert.variables:if var not in self._trainable_weights:self._non_trainable_weights.append(var)else:for var in variables:self._non_trainable_weights.append(var)super(BertLayer, self).build(input_shape)def call(self, inputs):inputs = [K.cast(x, dtype="int32") for x in inputs]input_ids, input_mask, segment_ids = inputsbert_inputs = dict(input_ids=input_ids, input_mask=input_mask, segment_ids=segment_ids)result = self.bert(inputs=bert_inputs, signature="tokens", as_dict=True)if self.output_representation == "pooled_output":pooled = result["pooled_output"]elif self.output_representation == "mean_pooling":result_tmp = result["sequence_output"]mul_mask = lambda x, m: x * tf.expand_dims(m, axis=-1)masked_reduce_mean = lambda x, m: tf.reduce_sum(mul_mask(x, m), axis=1) / (tf.reduce_sum(m, axis=1, keepdims=True) + 1e-10)input_mask = tf.cast(input_mask, tf.float32)pooled = masked_reduce_mean(result_tmp, input_mask)elif self.output_representation == "sequence_output":pooled = result["sequence_output"]return pooledcs 12345678910111213141516def compute_mask(self, inputs, mask=None):if self.output_representation == 'sequence_output':inputs = [K.cast(x, dtype="bool") for x in inputs]mask = inputs[1]return maskelse:return Nonedef compute_output_shape(self, input_shape):if self.output_representation == "sequence_output":return (input_shape[0][0], input_shape[0][1], self.output_size)else:return (input_shape[0][0], self.output_size)cs
4. 모델 빌드
123456789101112131415161718192021222324252627282930313233import kerasdef build_model(max_seq_length, tf_hub, n_classes, n_fine_tune):in_id = keras.layers.Input(shape=(max_seq_length,), name="input_ids")in_mask = keras.layers.Input(shape=(max_seq_length,), name="input_masks")in_segment = keras.layers.Input(shape=(max_seq_length,), name="segment_ids")bert_inputs = [in_id, in_mask, in_segment]bert_output = BertLayer(n_fine_tune_layers=n_fine_tune, tf_hub = tf_hub, output_representation = 'mean_pooling', trainable = True)(bert_inputs)drop = keras.layers.Dropout(0.3)(bert_output)dense = keras.layers.Dense(256, activation='sigmoid')(drop)drop = keras.layers.Dropout(0.3)(dense)dense = keras.layers.Dense(64, activation='sigmoid')(drop)pred = keras.layers.Dense(n_classes, activation='softmax')(dense)model = keras.models.Model(inputs=bert_inputs, outputs=pred)Adam = keras.optimizers.Adam(lr = 0.0005)model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam, metrics=['sparse_categorical_accuracy'])model.summary()return modeldef initialize_vars(sess):sess.run(tf.local_variables_initializer())sess.run(tf.global_variables_initializer())sess.run(tf.tables_initializer())K.set_session(sess)n_classes = len(label_encoder.classes_)n_fine_tune_layers = 48model = build_model(max_seq_length, bert_path, n_classes, n_fine_tune_layers)# Instantiate variablesinitialize_vars(sess)cs 1model.trainable_weightscs
5. 토큰화(Tokenization)
1234567891011121314151617181920212223242526272829303132class InputExample(object):"""A single training/test example for simple sequence classification."""def __init__(self, guid, text_a, text_b=None, label=None):"""Constructs a InputExample.Args:guid: Unique id for the example.text_a: string. The untokenized text of the first sequence. For singlesequence tasks, only this sequence must be specified.text_b: (Optional) string. The untokenized text of the second sequence.Only must be specified for sequence pair tasks.label: (Optional) string. The label of the example. This should bespecified for train and dev examples, but not for test examples."""self.guid = guidself.text_a = text_aself.text_b = text_bself.label = labeldef create_tokenizer_from_hub_module(tf_hub):"""Get the vocab file and casing info from the Hub module."""bert_module = hub.Module(tf_hub)tokenization_info = bert_module(signature="tokenization_info", as_dict=True)vocab_file, do_lower_case = sess.run([tokenization_info["vocab_file"],tokenization_info["do_lower_case"],])return FullTokenizer(vocab_file=vocab_file, do_lower_case=do_lower_case)cs 123456789101112131415161718192021222324252627282930313233343536def convert_single_example(tokenizer, example, max_seq_length=256):"""Converts a single `InputExample` into a single `InputFeatures`."""if isinstance(example, PaddingInputExample):input_ids = [0] * max_seq_lengthinput_mask = [0] * max_seq_lengthsegment_ids = [0] * max_seq_lengthlabel = 0return input_ids, input_mask, segment_ids, labeltokens_a = tokenizer.tokenize(example.text_a)if len(tokens_a) > max_seq_length - 2:tokens_a = tokens_a[0 : (max_seq_length - 2)]tokens = []segment_ids = []tokens.append("[CLS]")segment_ids.append(0)for token in tokens_a:tokens.append(token)segment_ids.append(0)tokens.append("[SEP]")segment_ids.append(0)#print(tokens)input_ids = tokenizer.convert_tokens_to_ids(tokens)# The mask has 1 for real tokens and 0 for padding tokens. Only real# tokens are attended to.input_mask = [1] * len(input_ids)# Zero-pad up to the sequence length.while len(input_ids) < max_seq_length:input_ids.append(0)input_mask.append(0)segment_ids.append(0)assert len(input_ids) == max_seq_lengthassert len(input_mask) == max_seq_lengthassert len(segment_ids) == max_seq_lengthreturn input_ids, input_mask, segment_ids, example.labelcs 123456789101112131415161718192021222324252627def convert_examples_to_features(tokenizer, examples, max_seq_length=256):"""Convert a set of `InputExample`s to a list of `InputFeatures`."""input_ids, input_masks, segment_ids, labels = [], [], [], []for example in tqdm_notebook(examples, desc="Converting examples to features"):input_id, input_mask, segment_id, label = convert_single_example(tokenizer, example, max_seq_length)input_ids.append(input_id)input_masks.append(input_mask)segment_ids.append(segment_id)labels.append(label)return (np.array(input_ids),np.array(input_masks),np.array(segment_ids),np.array(labels).reshape(-1, 1),)def convert_text_to_examples(texts, labels):"""Create InputExamples"""InputExamples = []for text, label in zip(texts, labels):InputExamples.append(InputExample(guid=None, text_a=" ".join(text), text_b=None, label=label))return InputExamplescs 123456789101112# 토크나이저 초기화tokenizer = create_tokenizer_from_hub_module(bert_path)# 데이터를 입력포멧으로 변환train_examples = convert_text_to_examples(train_text, train_label)val_examples = convert_text_to_examples(val_text, val_label)# 피쳐로 변환(train_input_ids, train_input_masks, train_segment_ids, train_labels) = convert_examples_to_features(tokenizer, train_examples, max_seq_length=max_seq_length)(val_input_ids, val_input_masks, val_segment_ids, val_labels) = convert_examples_to_features(tokenizer, val_examples, max_seq_length=max_seq_length)cs
6. 모델 훈련
1234567891011121314151617from keras.callbacks import EarlyStoppingBATCH_SIZE = 256MONITOR = 'val_sparse_categorical_accuracy'print('BATCH_SIZE is {}'.format(BATCH_SIZE))e_stopping = EarlyStopping(monitor=MONITOR, patience=3, verbose=1, mode='max', restore_best_weights=True)callbacks = [e_stopping]history = model.fit([train_input_ids, train_input_masks, train_segment_ids],train_labels,validation_data = ([val_input_ids, val_input_masks, val_segment_ids], val_labels),epochs = 10,verbose = 1,batch_size = BATCH_SIZE,callbacks= callbacks)cs 256 batch, 10 epochs
colab GPU로 1시간 좀 넘게 걸렸습니다.
결과
BERT 시리즈
BERT는 무엇인가, 동작 구조
2020/02/12 - [SW개발/AI Development] - [BERT] BERT에 대해 쉽게 알아보기1 - BERT는 무엇인가, 동작 구조
BERT 사전훈련시키기
2020/03/26 - [SW개발/AI Development] - [BERT] BERT에 대해 쉽게 알아보기2 - BERT 사전훈련시키기(텐서플로우)
BERT tf-hub이용, 캐글 분류 문제
2020/03/27 - [SW개발/AI Development] - [BERT] BERT에 대해 쉽게 알아보기3 - tf-hub BERT layer를 이용한 캐글 분류문제
BERT 파인튜닝
2020/03/30 - [SW개발/AI Development] - [BERT] BERT에 대해 쉽게 알아보기4 - BERT 파인튜닝
[References]
Keras.metrics.SparseCategoricalAccuracy
BERT fine-tuning for Tensorflow 2.0 with Keras API
Simple Text Classification using BERT in TensorFlow Keras 2.0
BERT Text Classification in 3 Lines of Code Using Keras(ktrain)
'✨ AI > NLP' 카테고리의 다른 글
[NLP] 자연어처리 - 한국어 임베딩 (0) 2020.10.30 [NLP] 자연어처리 - 한국어 전처리를 위한 기법들 (4) 2020.10.22 [BERT] BERT에 대해 쉽게 알아보기4 - BERT 파인튜닝 (12) 2020.03.30 [BERT] BERT에 대해 쉽게 알아보기2 - colab으로 BERT Pretraining(수정) (17) 2020.03.26 [BERT] BERT에 대해 쉽게 알아보기1 - BERT는 무엇인가, 동작 구조 (2) 2020.02.12