参考リンク
PythonでWord2vecを使い類似語を表示するスクリプトを作った【自然言語処理, gensim】
3.Conclusion
我々は医療に近い領域の単語埋め込みに対して、化学特許を通じて訓練された単語埋め込みの質について研究を行った。我々の実験によって、特許埋め込みは他の埋め込みに対して、化学NERの優れたF1値で示されるように、外来的に優れていることが示されている。相関性と類似性分析によって、これらがまた内省的にも優れていることが示され、このことによって化学領域の理解が深まった。また、文脈化した単語埋め込み(ELMo)は全体的により良い結果を生み出し、一般的ではあるが、大規模のPubMed ELMo埋め込み(これは生命化学領域の全体を覆う)は化学領域において穏当な結果を生み出した。
付録
ここに2種類の可視化データを示す。1つ目は単語埋め込みデータで、もう1つは表4における分子類似度ランキングである。化学構造式を作るため、そして分子類似度を測るために、RDKitを利用した。我々はリスト中の用語を知らないふりをして、規格化も行っていない。単語埋め込みの可視化データに対して(単語埋め込みの範囲を全てカバーしている)SCAIテストコーパスから100語ランダムに選んで取り上げた。これと並行して、t-SNEアルゴリズムを用いて、単語埋め込みにおける200次元を2次元にまで減少させた。
可視化データは概して論文中で考察したものと合致する。類似度ランキングはELMo埋め込みランキングにより合致する。同様に、ELMoによる単語埋め込みの可視化データは他のものがかなり分散しているのに対して、収集する傾向がみられた。
表3に示すように、大規模なPubMed
Word2Vecによる埋め込みは基準モデルであるMat2VecよりもわずかばかりF1値が増加している。これら2つとも製薬目的のWord2Vec埋め込みには大きく打ち負かされている。特許目的の化学Word2Vecを用いるとWord2Vec埋め込みの中で最大のF1値を叩きだした。もっとも良い値が得られたのはELMoを利用したときで、それはWord2Vecよりも更に大きな値を叩きだし、興味深いことにF1値は72.41%に達した。これもまた興味深いことであるが、PubMed
ELMoについても十分な専門知識を持ち、F1値は70.15%に及んだ。
内省的評価:量的な、もしくは内省的評価について、我々は比較可能な埋め込みを行うために次のような戦略を取った。我々はWord2Vecはめ込みの語彙をテストコーパスの語彙に制限した(ストップとファンクションキーワードを遮断した)。これら制限されたWord2Vec埋め込みをELMo埋め込みと比較するために、我々はテストコーパスの内容語ごとにELMo埋め込みを計算し、そして、アドホックの単純なコーパスレベルのWord2Vec埋め込みを引き出すための埋め込み全てについて平均した。そのような方法は潜在的に文脈化された情報を捨て去る一方で、文脈化された単語埋め込みが似たような単語埋め込みを単語に割り当て、それらの単語がその文中で似たような文法や意味的な役割を果たすと仮定すれば、この単語埋め込みは統語論と意味論における言葉の型を未だに保全する。この単語埋め込みの次元は特異値分解SVDによって減らされ、200,420,1024の次元で標準化された。3つの分析を実行した。
類似性分析:我々は製薬物質としてテストコーパスで言及されている「イブプロフェン」を選び、コサイン計算を用いて上位10個の類似の単語を回収した。理想的にはイブプロフェンは薬なので、類似性テストで期待されるのは薬、もしくは化学物質の名前である。
得られた結果は注意して、そして固有表現認識NERモデルの結果と並べられた。表4が示すように、化学特許埋め込みはより一般的な、もしくは特許目的ではない相手よりより成績を示す。そのうえ、ELMo埋め込みは更に良い結果を示す。Mat2Vec、Drug、PubMed埋め込みは一般的な名詞「drugs」、略語「mg」、形容詞「topical」、動詞「mimics」を返している一方、化学特許Word2VecとELMo埋め込みは製薬名、もしくは化学物質名のみを返した。同様に興味深いことに上位に入っている類似の物質名(アスピリン、タクリン、アトロピン)はいくらか同様に抗炎症性の性質を示す。
一致分析:これらのリストが意味論的にどれほど整列しているかを確認すると、傾向は大部分は確認された。化学用語は複数の意味を持つ傾向があるので、これらの語を正常化するためにドラッグバンクを利用した。終わりに、どれくらいの埋め込みが「イブプロフェン」という語の理解について一致しているか、結果として起こる規格化された表の類似性を測定した。
表5に示されるように化学特許W2VとELMo埋め込みは大体は合致している(それぞれ、0.43と0.54)。その一方で、製薬埋め込みとMat2Vec埋め込みは他の埋め込みと合致していないように見える。興味深いことにPubMed
Word2Vec埋め込みは化学特許埋め込みと予想されるよりも高く合致している。
相関性分析:最後に埋め込み間の相関性の程度について測定を行った。図3に示されるように、ELMo同士は(0.91)^5という高い相関性がある。化学特許埋め込みとPubMed
Word2Vecについては全ての埋め込みについて適度に相関性が見られ、その一方で、Mat2Vec埋め込みと製薬埋め込み間はより低い相関性を示した。
題名:Word Embeddings for Chemical Patent Natural Language Processing
著者:Camilo Thorne, Saber Akhondi
発表年:2020年10月
リンク先:https://arxiv.org/abs/2010.12912
Abstract
我々はを既知の医学用単語埋め込み技術を背景として、化学特許における単語埋め込み技術の評価を行い、その結果は、医学用単語埋め込みより外因的にも、内因的にも優れていた。我々はまた、文脈化した埋め込み技術によって、相対的に小さなゴールドスタンダードテストを超えたこの領域において合理的な性能を持った予言可能なモデルを構築することができることを示した。
1.Introduction
化学産業は疑うことなく新しい化合物の発見に依存している。しかしながら、新しい化合物は最初に特許文献に現れ、その数か月とか数年後に科学論文に出版される。このようにしてほとんどの化学物質は特許においてのみただちに入手可能となる。新たな化学に関する特許申請書の数は劇的に増加しているので、現在、自然言語処理によって化学の特許申請書から自動的に情報を抽出することは重要である。
この努力において鍵となる道具は単語埋め込み技術である。埋め込み技術は固有表現抽出(NER)のような最新式のニューラルモデルにおいて単語や文章やテキストレベルの特徴を抽出するのに重要である。大規模なWord2Vecと文脈化された埋め込み技術は医療と製薬領域のために開発された。Mat2Vecのような分析化学のための小規模な埋め込み技術もまた提案されている。それらのような埋め込み技術は全て科学論文(PubMedコーパス)を用いて訓練されている。つい最近では、Zhaiらが化学の固有表現抽出の学習と適用に成功し、化学特許を学習したELMo埋め込み技術、いわゆるCheMU埋め込み技術の文脈化を行った。この論文では、我々は以前のものに対する化学特許の埋め込み技術の品質を評価に関する問題点を取り扱う。この種の比較においては2つの手法が通例である。1つは、予想における各単語埋め込みの効果(この論文では化学固有表現抽出)といった外省的評価であり、もう一つは分配表現の質を定量的に分析する際の内省的評価は化学用語を指定している。この目的のために、ある化学用語に埋め込まれている用語をうえから似ている順に、例えば、イブプロフェンであれば抗炎症性薬といったように、10個比較することが習慣的になされている。我々は化学特許の単語埋め込みが以前のものより外省的にも、内省的にも優れていることを示す。
自然言語処理(Natural Language Processing)、特に機械学習を用いたNLP処理でまず最初に行うべきこと。単語をベクトル化することで、単語の概念を示し、数学的な処理を行うことができるようになる。
【単語埋め込み技術(分散表現)の種類】
・word2vec
基本的な考え方はJohn Rupert Firthの"a word is characterized by the company it keeps"(単語はその周辺によって特徴付けられる)という考え方による。 skip-gramとCBOWモデルを利用。
・Glove
word2vecでは周辺単語から言葉の意味(ベクトル)を推定するが、GloVeではコーパス全体から得られる単語間の共起行列を用いた最適化関数 (重み付き最小二乗法) で学習する。
・fasttext
FastTextは2016年にFacebookによって最初に発表された素のWord2Vecモデルを拡張および改善したもの。各単語をBag of Character n-gram(サブワードモデル)とみなしてベクトル化する。計算量は多くなるがサブワードモデルのおかげで珍しい単語が出現しても対応できる可能性が高い。
・Elmo
事前学習によって文脈を考慮した単語表現を可能にする手法。 双方向言語モデルを用いることで実現される。双方向を用いることで語順が順方向だけではなく、逆方向に対しても学習することできる。
・Indra
情報収集中
分散ベクトルを作った後は、主成分分析(PCA)やT-Distributed Stochastic Neighbor Embedding(t-sne)で視覚化(評価)することができる。
参考リンク
論文メモ:Deep contextualized word representations(ELmo)
Building a model from natural language with INDRA
Word embeddings in 2020. Review with code examples
問. popular-names.txtは,アメリカで生まれた赤ちゃんの「名前」「性別」「人数」「年」をタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,popular-names.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ
各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ.
解答例
import pandas as pd
df = pd.read_table('./popular-names.txt', header=None, sep='\t', names=['name', 'sex', 'number', 'year'])
#col1の定義 dfの中からnameを取り出して、col1と定義
col1 = df['name']
#pandasのto_csv関数を使う
col1.to_csv('./col1.txt', index=False)
#col1の冒頭5行を書き出す
print(col1.head())
出力
0 Mary
1 Anna
2 Emma
3 Elizabeth
4 Minnie
5 Margaret
6 Ida
7 Alice
8 Bertha
9 Sarah
Name: name, dtype: object
確認:ubuntuで実行
cut -f 1 ./popular-names.txt > ./col1_chk.txt
cat ./col1_chk.txt | head -n 5
出力
Mary
Anna
Emma
Elizabeth
Minnie
参考リンク
問. popular-names.txtは,アメリカで生まれた赤ちゃんの「名前」「性別」「人数」「年」をタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,popular-names.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.
タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.
解答例:UNIXコマンドでの確認
sed -e 's/\t/ /g' ./popular-names.txt | tail -n 5
出力
Benjamin M 13381 2018
Elijah M 12886 2018
Lucas M 12585 2018
Mason M 12435 2018
Logan M 12352 2018
unixの標準コマンドストリームエディタsedを用いた。
sed -e 's/AAA/BBB/g' ファイル名
ファイルの中のAAAをBBBで最初(s/)から終わり(/g)まで置換する。
今回は\t(タブ)を//(スペース)に変換している。
その後、パイプ(|)でtail -n 5(末尾から5行表示する)につないでいる。
参考リンク
【言語処理100本ノック 2020】第2章: UNIXコマンド
問. popular-names.txtは,アメリカで生まれた赤ちゃんの「名前」「性別」「人数」「年」をタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,popular-names.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.
行数をカウントせよ.確認にはwcコマンドを用いよ.
解答例
unixのwgetコマンドを利用してテキストをダウンロードする。
wget https://nlp100.github.io/data/popular-names.txt
もしくは、curlコマンドも利用できるが、その場合、ファイル名を指定してやる。
(指定しなければ、そのままファイルの中身を表示する。)
curl https://nlp100.github.io/data/popular-names.txt > name.txt
(popular-names.txtの中身をname.txtというファイル名で保存。リダイレクション「>」はファイル上書きを行い、「>>」はファイル追記を行う。)
その後、Pythonで次のプラグラムを実行する。
#pandasをインポート
import pandas as pd
#dfの定義。read_tableを使ってテキストファイルを読み込む。
df = pd.read_table('./popular-names.txt', header=None, sep='\t', names=['name', 'sex', 'number', 'year'])
# dfの長さを「len」(=length)で表示。
print(len(df))
出力
2780
len関数は文字列の長さなどを読み込むが、今回はテーブルデータなので、行数を数える。
unixコマンドでの確認
wc -l ./popular-names.txt
出力
2780 ./popular-names.txt
参考リンク
【言語処理100本ノック 2020】第2章: UNIXコマンド
Pandasのread_tableでtxtファイルを読み込む方法
データベースの用語を理解しよう 「テーブル」「レコード」「カラム」「フィールド」とは?
問. スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.適当な英語の文(例えば”I couldn’t believe that I could actually understand what I was reading : the phenomenal power of the human mind .”)を与え,その実行結果を確認せよ.
解答例
#ライブラリrandomを読み込み
import random
#関数shuffleの定義
def shuffle(words):
result = []
for word in words.split():
if len(word) > 4: # 長さが4超であればシャッフル
word = word[:1] + ''.join(random.sample(word[1:-1], len(word) - 2)) + word[-1:]
result.append(word)
return ' '.join(result)
#読み込む文章(words)の定義
words = "I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind ."
#shuffleの出力(ans)を定義
ans = shuffle(words)
#ansの表示
print(ans)
出力(ランダムにシャッフルしているので、答えは毎回変わります)
I clundo't beevile that I could acultaly udntsnaerd what I was rnediag : the pnmnheaoel poewr of the hamun mind .
参考リンク
問. 与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.
英小文字ならば(219 - 文字コード)の文字に置換
その他の文字はそのまま出力
この関数を用い,英語のメッセージを暗号化・復号化せよ
解答例
#暗号文cipherの定義
def cipher(str):
rep = [chr(219 - ord(x)) if x.islower() else x for x in str]
return ''.join(rep)
#message(元の文)の定義
message = 'the quick brown fox jumps over the lazy dog'
#message(暗号化したもの)の定義
message = cipher(message)
#暗号化した文の表示
print('暗号化:', message)
#message(復号化したもの)の定義
message = cipher(message)
#複合化した文の定義
print('復号化:', message)
出力
暗号化: gsv jfrxp yildm ulc qfnkh levi gsv ozab wlt
復号化: the quick brown fox jumps over the lazy dog
参考リンク
問. 引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y=”気温”, z=22.4として,実行結果を確認せよ.
解答例
def generate_sentence(x, y, z):
print(f'{x}時のとき{y}は{z}℃')
generate_sentence(12, '気温', 22.4)
出力
12時のとき気温は22.4℃
ちなみにprintのあとのf(f文字列)を取って、「print('{x}時のとき{y}は{z}℃')」と書くと、print文の内容がそのまま「{x}時のとき{y}は{z}℃」と出力されてしまうので、注意する。
参考リンク
Pythonのf文字列(フォーマット済み文字列リテラル)の使い方
問. “paraparaparadise”と”paragraph”に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,’se’というbi-gramがXおよびYに含まれるかどうかを調べよ.
解答例
#前問のngramを使いまわす
def ngram(n, lst):
return list(zip(*[lst[i:] for i in range(n)]))
#対象となる文字列の定義
str1 = 'paraparaparadise'
str2 = 'paragraph'
#setを使って集合を定義する
X = set(ngram(2, str1))
Y = set(ngram(2, str2))
#集合の演算を行う
union = X | Y
intersection = X & Y
difference = X - Y
#集合の表示
print('X:', X)
print('Y:', Y)
print('和集合:', union)
print('積集合:', intersection)
print('差集合:', difference)
#「<=」で部分集合について判定している
print('Xにseが含まれるか:', {('s', 'e')} <= X)
print('Yにseが含まれるか:', {('s', 'e')} <= Y)
出力
X: {('a', 'd'), ('r', 'a'), ('a', 'p'), ('i', 's'), ('a', 'r'), ('p', 'a'), ('s', 'e'), ('d', 'i')}
Y: {('r', 'a'), ('a', 'p'), ('a', 'r'), ('g', 'r'), ('p', 'a'), ('p', 'h'), ('a', 'g')}
和集合: {('a', 'd'), ('r', 'a'), ('a', 'p'), ('i', 's'), ('a', 'r'), ('g', 'r'), ('p', 'a'), ('s', 'e'), ('p', 'h'), ('a', 'g'), ('d', 'i')}
積集合: {('a', 'p'), ('p', 'a'), ('a', 'r'), ('r', 'a')}
差集合: {('s', 'e'), ('a', 'd'), ('d', 'i'), ('i', 's')}
Xにseが含まれるか: True
Yにseが含まれるか: False
参考リンク
Python, set型で集合演算(和集合、積集合や部分集合の判定など)
問. 与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,”I am an NLPer”という文から単語bi-gram,文字bi-gramを得よ
解答例
#ngramの定義
def ngram(n, lst):
#要素(単語であったり、文字であったり)ごとにいくつかに区切って、zip関数に渡す
return list(zip(*[lst[i:] for i in range(n)]))
#対象となる文字列を読み込む
str = 'I am an NLPer'
#単語bi-gramの定義。splitで単語に区切っている
words_bi_gram = ngram(2, str.split())
#文字bi-gramの定義。strを1文字ずつ読み込んで、2つずつzip関数に渡す。
chars_bi_gram = ngram(2, str)
#単語bi-gramと文字bi-gramの結果の表示
print('単語bi-gram:', words_bi_gram)
print('文字bi-gram:', chars_bi_gram)
出力
単語bi-gram: [('I', 'am'), ('am', 'an'), ('an', 'NLPer')]
文字bi-gram: [('I', ' '), (' ', 'a'), ('a', 'm'), ('m', ' '), (' ', 'a'), ('a', 'n'), ('n', ' '), (' ', 'N'), ('N', 'L'), ('L', 'P'), ('P', 'e'), ('e', 'r')]
ポイント
・zip関数を使って、リストを作る
・zip関数はfor loopと一緒に使う。
参考リンク
Python, zip関数の使い方: 複数のリストの要素をまとめて取得
問.“Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.”という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.
解答例
import re
str = 'Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.'
str = re.sub('[,\.]', '', str) # ,と.を除去
splits = str.split() # スペースで区切って単語ごとのリストを作成
ans = [len(i) for i in splits]#lenは文字数をカウントする関数
print(ans)
出力
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
では、「,と.」を除去せずにプログラムを実行するとどうなるか。
プログラム
import re
str = 'Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.'
splits = str.split()
ans = [len(i) for i in splits]
print(ans)
出力
[3, 1, 4, 1, 6, 9, 2, 7, 5, 3, 5, 8, 9, 7, 10]
,や.も1文字として数えてしまっている。呼び出しているライブラリreは正規表現(re:regular expressions)に関するもの。
別解
raw_text = 'Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.'
#raw_text中の.と,を""(なにもなし)に置き換える
text = raw_text.replace('.', '').replace(',', '')
#textを区切り文字で分割し(text.split())、文字数を数えるlen関数で最初から数えていく。
ans = [len(w) for w in text.split()]
print(ans)
出力
[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
ちなみに「print(text)」に対する出力はこうなる。
出力
Now I need a drink alcoholic of course after the heavy lectures involving quantum mechanics
参考リンク
Pythonのモジュールreで正規表現操作を行う方法【初心者向け】
問. 「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ
解答例
#str2,str2でパトカーとタクシーを定義
str1 = "パトカー"
str2 = "タクシー"
#「パタトクカシーー」の出力先となる空の行列を入れたstr3を作る
str3 = ""
#zip関数を使ってstr3にstr1とstr2の文字を交互に入れていく
str4 = str3.join([i+j for i, j in zip(str1, str2)])
#str4を表示
print(str4)
出力
パタトクカシーー
では、str2とstr3を入れ替えるとどうなるのか。
str1 = "パトカー"
str2 = ""
str3 = "タクシー"
str4 = str3.join([ i+j for i,j in zip(str1,str2)])
print(str4)
出力
プロセスは終了コード 0 で完了しました
何も出ませんね。str2が空行列のため、str4が作られなかった。
確認のためにstr2を空行列ではなく、「XXXXXX」とするとどうなるでしょうか。
str1 = "パトカー"
str2 = "XXXXXX"
str3 = "タクシー"
str4 = str3.join([ i+j for i,j in zip(str1,str2)])
print(str4)
出力
パXタクシートXタクシーカXタクシーーX
タクシーで1つの語なので、うまく混ざりませんでした。
参考リンク
Python, zip関数の使い方: 複数のリストの要素をまとめて取得
問「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.
解答例
#文字列の定義(別にstrでなくても好きな文字で良い)
str = "ぱたとくかしーー"
#行の最初から最後まで1文字空けて表示
ans1 = str[0:8:2]
print(ans1)
#行の2文字目から最後まで1文字空けて表示
ans2=str[1:8:2]
print(ans2)
出力
ぱとかー
たくしー
解答例2:行の最初と最後の文字は省略することができる。
str = "ぱたとくかしーー"
ans1 = str[::2]
print(ans1)
ans2=str[1::2]
print(ans2)
Pythonによる文字列の数え方
何番目の文字と数えるよりは、今回の場合であれば、
「^パ^タ^ト^ク^カ^シ^ー^ー^ 」
のように1文字ずつ空けて(^:記号)、0から数え始めると良い。
(1がスタートではありません。)
参考リンク
問:文字列”stressed”の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.
解答例
str = 'stressed'
#[::-1] 「先頭」から「末尾」まで「逆順の移動幅1」で
ans = str[::-1]
print(ans)
出力
desserts
別解
str = "stressed"
#for loopを逆順で回す
for word in reversed(str):
print(word)
出力
d
e
s
s
e
r
t
s
答えが縦書きになった
参考リンク