def create_vocab_text(): TEXT = torchtext.data.Field(sequential=True, tokenize=tokenizer_with_preprocessing, use_vocab=True, lower=False, include_lengths=True, batch_first=True, fix_length=max_length, init_token="[CLS]", eos_token="[SEP]", pad_token='[PAD]', unk_token='[UNK]') LABEL = torchtext.data.Field(sequential=False, use_vocab=False) train_val_ds, test_ds = torchtext.data.TabularDataset.splits( path=DATA_PATH, train='train_dumy.csv', test='test_dumy.csv', format='csv', fields=[('Text', TEXT), ('Label', LABEL)]) vocab_bert, ids_to_tokens_bert = load_vocab(vocab_file=VOCAB_FILE) TEXT.build_vocab(train_val_ds, min_freq=1) TEXT.vocab.stoi = vocab_bert pickle_dump(TEXT, PKL_FILE) return TEXT
def __init__(self, data_dir=r'./', bert_dir=r'./pytorch_advanced/nlp_sentiment_bert/'): self.data_dir = data_dir self.bert_dir = bert_dir self.tokenizer_bert = BertTokenizer( vocab_file=self.bert_dir + "vocab/bert-base-uncased-vocab.txt", do_lower_case=True) self.vocab_bert, self.ids_to_tokens_bert = load_vocab( vocab_file=self.bert_dir + "vocab/bert-base-uncased-vocab.txt") config = get_config(file_path=self.bert_dir + "weights/bert_config.json") self.net_bert = BertModel(config) self.net_bert = set_learned_params(self.net_bert, weights_path=self.bert_dir + "weights/pytorch_model.bin")
def get_chABSA_DataLoaders_and_TEXT(max_length=256, batch_size=32): """IMDbのDataLoaderとTEXTオブジェクトを取得する。 """ def preprocessing_text(text): # 半角・全角の統一 text = mojimoji.han_to_zen(text) # 改行、半角スペース、全角スペースを削除 text = re.sub('\r', '', text) text = re.sub('\n', '', text) text = re.sub(' ', '', text) text = re.sub(' ', '', text) # 数字文字の一律「0」化 text = re.sub(r'[0-9 0-9]+', '0', text) # 数字 # カンマ、ピリオド以外の記号をスペースに置換 for p in string.punctuation: if (p == ".") or (p == ","): continue else: text = text.replace(p, " ") # ピリオドなどの前後にはスペースを入れておく text = text.replace(".", " . ") text = text.replace(",", " , ") return text # 前処理と単語分割をまとめた関数を定義 # 単語分割の関数を渡すので、tokenizer_bertではなく、tokenizer_bert.tokenizeを渡す点に注意 def tokenizer_with_preprocessing(text, tokenizer=tokenizer_bert.tokenize): text = preprocessing_text(text) ret = tokenizer(text) # tokenizer_bert return ret # データを読み込んだときに、読み込んだ内容に対して行う処理を定義します max_length = 256 TEXT = torchtext.data.Field(sequential=True, tokenize=tokenizer_with_preprocessing, use_vocab=True, lower=False, include_lengths=True, batch_first=True, fix_length=max_length, init_token="[CLS]", eos_token="[SEP]", pad_token='[PAD]', unk_token='[UNK]') LABEL = torchtext.data.Field(sequential=False, use_vocab=False) # フォルダ「data」から各tsvファイルを読み込みます train_ds, val_ds = torchtext.data.TabularDataset.splits(path='./data/', train='train.tsv', test='test.tsv', format='tsv', fields=[('Text', TEXT), ('Label', LABEL)]) # torchtextで日本語ベクトルとして日本語学習済みモデルを読み込む #japanese_fastText_vectors = Vectors(name='./data/model.vec') # ベクトル化したバージョンのボキャブラリーを作成します #TEXT.build_vocab(train_ds, vectors=japanese_fastText_vectors, min_freq=5) # DataLoaderを作成します(torchtextの文脈では単純にiteraterと呼ばれています) train_dl = torchtext.data.Iterator(train_ds, batch_size=batch_size, train=True) val_dl = torchtext.data.Iterator(val_ds, batch_size=batch_size, train=False, sort=False) vocab_bert, ids_to_tokens_bert = load_vocab(vocab_file="./vocab/vocab.txt") TEXT.build_vocab(train_ds, min_freq=1) TEXT.vocab.stoi = vocab_bert return train_dl, val_dl, TEXT
def _load_vocab(self, vocab_file): vocab, ids_to_tokens = load_vocab(vocab_file) return vocab, ids_to_tokens
def DataLoader(max_length=256, batch_size=32): """IMDbのDataLoaderとTEXTオブジェクトを取得する。 """ # 乱数のシードを設定 torch.manual_seed(0) np.random.seed(0) random.seed(0) # 単語分割用のTokenizerを用意 tokenizer_bert = BertTokenizer(vocab_file=VOCAB_FILE, do_lower_case=False) def preprocessing_text(text): # 半角・全角の統一 text = mojimoji.han_to_zen(text) # 改行、半角スペース、全角スペースを削除 text = re.sub('\r', '', text) text = re.sub('\n', '', text) text = re.sub(' ', '', text) text = re.sub(' ', '', text) text = re.sub("\"", '', text) # 数字文字の一律「0」化 text = re.sub(r'[0-9 0-9]+', '0', text) # 数字 # カンマ、ピリオド以外の記号をスペースに置換 for p in string.punctuation: if (p == ".") or (p == ","): continue else: text = text.replace(p, " ") return text # 前処理と単語分割をまとめた関数を定義 # 単語分割の関数を渡すので、tokenizer_bertではなく、tokenizer_bert.tokenizeを渡す点に注意 def tokenizer_with_preprocessing(text, tokenizer=tokenizer_bert.tokenize): text = preprocessing_text(text) ret = tokenizer(text) # tokenizer_bert return ret # データを読み込んだときに、読み込んだ内容に対して行う処理を定義します max_length = 256 TEXT = torchtext.data.Field(sequential=True, tokenize=tokenizer_with_preprocessing, use_vocab=True, lower=False, include_lengths=True, batch_first=True, fix_length=max_length, init_token="[CLS]", eos_token="[SEP]", pad_token='[PAD]', unk_token='[UNK]') LABEL = torchtext.data.Field(sequential=False, use_vocab=False) # フォルダ「data」から各csvファイルを読み込みます # BERT用で処理するので、10分弱時間がかかります train_val_ds, test_ds = torchtext.data.TabularDataset.splits( path=DATA_PATH, train='train.csv', test='test.csv', format='csv', fields=[('Text', TEXT), ('Label', LABEL)]) vocab_bert, ids_to_tokens_bert = load_vocab(vocab_file=VOCAB_FILE) TEXT.build_vocab(train_val_ds, min_freq=1) TEXT.vocab.stoi = vocab_bert batch_size = 32 # BERTでは16、32あたりを使用する train_dl = torchtext.data.Iterator(train_val_ds, batch_size=batch_size, train=True) val_dl = torchtext.data.Iterator(test_ds, batch_size=batch_size, train=False, sort=False) # 辞書オブジェクトにまとめる dataloaders_dict = {"train": train_dl, "val": val_dl} return train_dl, val_dl, TEXT, dataloaders_dict
def main(): # define output dataframe sample = pd.read_csv("./data/sample_submission.csv") # データを読み込んだときに、読み込んだ内容に対して行う処理を定義します max_length = 256 TEXT = torchtext.data.Field(sequential=True, tokenize=tokenizer_with_preprocessing, use_vocab=True, lower=True, include_lengths=True, batch_first=True, fix_length=max_length, init_token="[CLS]", eos_token="[SEP]", pad_token='[PAD]', unk_token='[UNK]') LABEL1 = torchtext.data.Field(sequential=False, use_vocab=False) LABEL2 = torchtext.data.Field(sequential=False, use_vocab=False) LABEL3 = torchtext.data.Field(sequential=False, use_vocab=False) LABEL4 = torchtext.data.Field(sequential=False, use_vocab=False) LABEL5 = torchtext.data.Field(sequential=False, use_vocab=False) LABEL6 = torchtext.data.Field(sequential=False, use_vocab=False) # (注釈):各引数を再確認 # sequential: データの長さが可変か?文章は長さがいろいろなのでTrue.ラベルはFalse # tokenize: 文章を読み込んだときに、前処理や単語分割をするための関数を定義 # use_vocab:単語をボキャブラリーに追加するかどうか # lower:アルファベットがあったときに小文字に変換するかどうか # include_length: 文章の単語数のデータを保持するか # batch_first:ミニバッチの次元を先頭に用意するかどうか # fix_length:全部の文章を指定した長さと同じになるように、paddingします # init_token, eos_token, pad_token, unk_token:文頭、文末、padding、未知語に対して、どんな単語を与えるかを指定 # フォルダ「data」から各tsvファイルを読み込みます # BERT用で処理するので、10分弱時間がかかります temp_path = preprocessing.reformat_csv_header(path="./data", train_file="train.csv", test_file="test.csv") print("temp path {}".format(temp_path)) print("text {}".format(vars(TEXT))) train_val_ds, test_ds = torchtext.data.TabularDataset.splits( path=temp_path, train='train.csv', test='test.csv', format='csv', fields=[('Text', TEXT), ('toxic', LABEL1), ('severe_toxic', LABEL2), ('obscene', LABEL3), ('threat', LABEL4), ('insult', LABEL5), ('identity_hate', LABEL6)]) # torchtext.data.Datasetのsplit関数で訓練データとvalidationデータを分ける train_ds, val_ds = train_val_ds.split(split_ratio=0.8, random_state=random.seed(2395)) # BERTはBERTが持つ全単語でBertEmbeddingモジュールを作成しているので、ボキャブラリーとしては全単語を使用します # そのため訓練データからボキャブラリーは作成しません vocab_bert, ids_to_tokens_bert = load_vocab( vocab_file="./weights/bert-base-uncased-vocab.txt") # このまま、TEXT.vocab.stoi= vocab_bert (stoiはstring_to_IDで、単語からIDへの辞書)としたいですが、 # 一度bulild_vocabを実行しないとTEXTオブジェクトがvocabのメンバ変数をもってくれないです。 # ('Field' object has no attribute 'vocab' というエラーをはきます) # 1度適当にbuild_vocabでボキャブラリーを作成してから、BERTのボキャブラリーを上書きします TEXT.build_vocab(train_ds, min_freq=1) TEXT.vocab.stoi = vocab_bert # DataLoaderを作成します(torchtextの文脈では単純にiteraterと呼ばれています) batch_size = 16 # BERTでは16、32あたりを使用する train_dl = torchtext.data.Iterator(train_ds, batch_size=batch_size, train=True) val_dl = torchtext.data.Iterator(val_ds, batch_size=batch_size, train=False, sort=False) test_dl = torchtext.data.Iterator(test_ds, batch_size=batch_size, train=False, sort=False) # 辞書オブジェクトにまとめる dataloaders_dict = {"train": train_dl, "val": val_dl} print(vars(train_ds[0])) print(vars(test_ds[0])) # モデル設定のJOSNファイルをオブジェクト変数として読み込みます config = get_config(file_path="./weights/bert_config.json") # BERTモデルを作成します net_bert = BertModel(config) # BERTモデルに学習済みパラメータセットします net_bert = set_learned_params(net_bert, weights_path="./weights/pytorch_model.bin") for label in [ 'toxic', 'severe_toxic', 'obscene', 'threat', 'insult', 'identity_hate' ]: # モデル構築 net = BertTraining(net_bert) # 訓練モードに設定 net.train() print('done setup network') # 勾配計算を最後のBertLayerモジュールと追加した分類アダプターのみ実行 # 1. まず全部を、勾配計算Falseにしてしまう for name, param in net.named_parameters(): param.requires_grad = False # 2. 最後のBertLayerモジュールを勾配計算ありに変更 for name, param in net.bert.encoder.layer[-1].named_parameters(): param.requires_grad = True # 3. 識別器を勾配計算ありに変更 for name, param in net.cls.named_parameters(): param.requires_grad = True # 最適化手法の設定 # BERTの元の部分はファインチューニング optimizer = optim.Adam( [{ 'params': net.bert.encoder.layer[-1].parameters(), 'lr': 5e-5 }, { 'params': net.cls.parameters(), 'lr': 5e-5 }], betas=(0.9, 0.999)) # 損失関数の設定 criterion = nn.CrossEntropyLoss() # nn.LogSoftmax()を計算してからnn.NLLLoss(negative log likelihood loss)を計算 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # 学習・検証を実行する。1epochに20分ほどかかります num_epochs = 2 net_trained = train_model(net, dataloaders_dict, criterion, optimizer, num_epochs=num_epochs, label=label, device=device) # 学習したネットワークパラメータを保存します save_path = './weights/bert_fine_tuning_weights.pth' torch.save(net_trained.state_dict(), save_path) # テストデータ net_trained.eval() # モデルを検証モードに net_trained.to(device) # GPUが使えるならGPUへ送る predicts = [] for batch in tqdm(test_dl): # testデータのDataLoader # batchはTextとLableの辞書オブジェクト # GPUが使えるならGPUにデータを送る inputs = batch.Text[0].to(device) # 文章 # 順伝搬(forward)計算 with torch.set_grad_enabled(False): # BertForIMDbに入力 outputs = net_trained(inputs, token_type_ids=None, attention_mask=None, output_all_encoded_layers=False, attention_show_flg=False) _, preds = torch.max(outputs, 1) # ラベルを予測 preds = preds.cpu() preds = preds.numpy().tolist() predicts += preds sample[label] = predicts # save predictions if not os.path.exists("./submission"): os.mkdir("./submission") sample.to_csv("./submission/submission_Bert_{}_{}ep.csv".format( datetime.datetime.now().date(), num_epochs), index=False)
def _load_vocab(self, vocab_file): _v, _i2t = load_vocab(vocab_file) return _v, _i2t