Jupyter Notebook の CSS 要素を編集する
Jupyter Notebook の CSS 要素を編集する方法に関するTipsです。IPython.core.display
を用いて、次のようにCSS要素を編集できます。
from IPython.core.display import display, HTML display(HTML("<style>.cm-s-ipython span.cm-comment { color: red; }</style>"))
こんな感じでcssを操作できますhttps://t.co/0AfVR6SWcT pic.twitter.com/EmF1tqreNm
— u++ (@upura0) May 15, 2020
CSSを当てるClass名については、ブラウザの開発者ツールなどで確認できます。
Kaggle Notebook も公開しています。編集ページと公開ページでClass名が異なるので、公開ページでは色が編集されていません。
公開ページでも色を変えたい場合は、公開ページのClass名の要素を変更する次のコードを実行しておくと良いでしょう。
display(HTML("<style>.highlight .c1 { color: red; }</style>"))
「BERT応用勉強会」参加録 #xpaperchallenge
「BERT応用勉強会」にオンライン参加しました。簡単な発表概要と個人的な所感をメモしておきます。発表動画のアーカイブは、YouTubeで後日公開されるそうですました。slidoとYouTubeコメントでの質疑応答はSpreadsheetにまとめてみました。
nlpaper-challenge.connpass.com
- 医療言語処理へのBERTの応用 --BioBERT, ClinicalBERT, そして--
- Multilingual BERTの二言語領域適応に基づく対訳文同定
- BERTのMulti Modalタスクへの活用
- BERTをブラウザで動かしたい!―MobileBERTとTensorFlow.js―
- テキスト生成の評価 × BERT
- おわりに
医療言語処理へのBERTの応用 --BioBERT, ClinicalBERT, そして--
発表資料
概要
- BERTの登場が医療言語処理に与えた影響について、放射線科専攻医&NLPを専攻する大学院生の視点からサーベイ
- 医療ドメインに特化したBERTが存在
- 適用事例として、固有表現抽出+病名正規化で、辞書ベースからのf1スコアの大幅な改善
- その他に質問応答、要約、再入院予測、カルテの固有表現抽出+関係認識、含意関係認識など
- BERTを非言語医療データへ応用する「BeHRt」
- 各受診時の診断と年齢を[SEP]区切りで入力することで診断の埋め込み表現を獲得
- 言語データと非言語医療データの統合
- 言語データ:医療記録をBioBERT+BiGRUで要約して獲得したベクトル化
- 非言語医療データ:ICD、薬剤、処理のコードをWord2vecでベクトル化
- 東京大学の医療AI開発学講座が日本語診療記録で事前学習したBERTを公開
所感
- BERTの応用として医療ドメインに寄せていく中で、非言語情報を取り込もうとしている流れはとても興味深い
- 特定のドメインの中で重要視される非言語情報は存在するはずで、BERTに使える形に落とし込むアイディアは参考にできそう
Multilingual BERTの二言語領域適応に基づく対訳文同定
概要
- NLP2020でも発表したBERTを用いた研究の報告
- ニューラル機械翻訳に必要な質・量ともに優れた対訳文を、BERTを用いて獲得する
- ケーススタディとして、日米の特許文書データを利用
- 対訳文同定の具体的な手順については、論文の図1が分かりやすい
- Wikipediaで事前学習済のモデルを特許データで再学習する領域適用の工夫をしている
- さらに対訳文の分類器をfine-tuning
所感
- 実際にNLPに取り組む際にはデータセットの準備が大きな課題となるため、意義深いタスク
- タスクを解くための問題設計の工夫が面白かった
- 質疑応答の中でも言及されたが、教師あり学習の要素を排除できると、より活用しやすくなると感じた
- 教師あり学習の部分がどの程度性能に貢献するかの評価が気になった
BERTのMulti Modalタスクへの活用
発表資料
概要
- マルチモーダルな領域でのBERTについてのサーベイ
- 今回はVision+Languageに限定
- 各モーダルのEncode方法、事前学習の方法などの観点で、さまざまなモデルをまとめている
- VilBERT
- LXMERT
- VL-BERT
- Unicoder-VL
- UNITER
所感
- 「なんかいっぱいモデル出てるなあ」くらいの理解だったモデル群についてサラッと学べて良かった
- 個人的には、いまKaggleでマルチモーダルなコンペが開催された場合に、どんなwinner solutionになるかは気になる
- 2019年3月のコンペではモーダル別に特徴量を抽出&結合して勾配ブースティング決定木に突っ込むのが主流だった
BERTをブラウザで動かしたい!―MobileBERTとTensorFlow.js―
発表資料
概要
- TensorFlow.jsの「MobileBERT」のQ&Aモデルの紹介
- MobileBERT: モバイル端末向けに、汎用性と精度を保ちつつ軽量化・高速化したBERT
- 蒸留を用いたBERT-LARGEからの学習方法やアーキテクチャの工夫点、組み込み方や実例などをまとめている
所感
- ブラウザで動くウェブアプリケーションが手軽に作成できそうで、TensorFlow.jsが提供するNLPモデルには注目している(日本語のモデルの登場にも期待したい)
- 性能の高さを大量のハイパーパラメータで追求する流れとともに、蒸留などを用いた軽量化の流れも実用の面から間違いなく捨て置けないので、今後の動向を押さえておきたい
テキスト生成の評価 × BERT
発表資料
概要
- テキスト生成タスクの評価にBERTを利用する方法についてのサーベイ
- 意味での類似度や単語の一致度など、いろいろな評価観点が存在
- 現状は自動評価に加えて人手評価も実行する場合が多いが、コストの高さや評価の保証に課題がある
- 既存の著名な「BLEU」「ROUGE」などの評価指標は、意味的な違いを取れていない、タスク依存するなどの問題がある
- BERTを駆使した評価手法が提案されている
所感
- BERTScoreの論文はidfでの重み付けなどの工夫や広範な実験が網羅されている印象
- 文書をBERTでベクトル化してコサイン類似度で近傍を探索するような簡易的な検索システムを構築する上でも参考になると感じた(腰を据えて読み込みたい)
- BERTScoreはpip installできるらしいので、いろいろ遊んでみたい
おわりに
今回、YouTubeライブで参加しました。初のオンライン開催ながら「Cluster」+YouTubeライブという、なかなか野心的な挑戦だったと思います。素敵な勉強会をありがとうございました。
Profile
The content has been moved to the following page. Shotaro Ishihara
企業名認識のデータセット「JCLdic」で学習したEncoder-Decoderモデル
TISが公開している企業名認識のためのデータセット「JCLdic」*1を用いて、Encoder-Decoderモデルを学習させてみました。
結果と考察
学習・検証に利用していないデータに対して適応した結果を下図に示します。統計的な出現頻度に基づくので当然な気がしますが①「ヤ」→「ャ」に修正②「有限会社」を明示しない場合は「株式会社」を付与ーーしています。
Encoder-Decoderモデルを用いた正規化は、クックパッドのブログ*2を読んで以来、試してみたいと考えていました。
今回は簡単のため「JCLdic」をそのまま活用しましたが「株式会社」を前に付けるか後に付けるかなどは、統計的に処理するのは不可能なタスクなように感じます。学習前のtgt側のデータから「株式会社」「有限会社」などを削除しておくことで、会社名部分のみの正規化というタスクに変換する方が理にかなっていそうです。
実装
実装には「OpenNMT-py」*3を利用しました。学習に利用したNotebookなどは、GitHub*4で公開しています。
言語処理100本ノック 2020「49. 名詞間の係り受けパスの抽出」
問題文
問題の概要
問題文に提示された仕様に従って出力します。第5章は2015年版と同様なので、先駆者のコード*1を流用しつつ実装しました。
class Morph: def __init__(self, dc): self.surface = dc['surface'] self.base = dc['base'] self.pos = dc['pos'] self.pos1 = dc['pos1'] class Chunk: def __init__(self, morphs, dst): self.morphs = morphs # 形態素(Morphオブジェクト)のリスト self.dst = dst # 係り先文節インデックス番号 self.srcs = [] # 係り元文節インデックス番号のリスト def parse_cabocha(block): def check_create_chunk(tmp): if len(tmp) > 0: c = Chunk(tmp, dst) res.append(c) tmp = [] return tmp res = [] tmp = [] dst = None for line in block.split('\n'): if line == '': tmp = check_create_chunk(tmp) elif line[0] == '*': dst = line.split(' ')[2].rstrip('D') tmp = check_create_chunk(tmp) else: (surface, attr) = line.split('\t') attr = attr.split(',') lineDict = { 'surface': surface, 'base': attr[6], 'pos': attr[0], 'pos1': attr[1] } tmp.append(Morph(lineDict)) for i, r in enumerate(res): res[int(r.dst)].srcs.append(i) return res def convert(s): pl, nl = [], [c for c in s if '名詞' in [m.pos for m in c.morphs]] for i in range(len(nl) - 1): st1 = [''.join([m.surface if m.pos != '名詞' else 'X' for m in nl[i].morphs])] for e in nl[i + 1:]: dst, p = nl[i].dst, [] st2 = [''.join([m.surface if m.pos != '名詞' else 'Y' for m in e.morphs])] while int(dst) != -1 and dst != s.index(e): p.append(s[int(dst)]) dst = s[int(dst)].dst if len(p) < 1 or p[-1].dst != -1: mid = [''.join([m.surface for m in c.morphs if m.pos != '記号']) for c in p] pl.append(st1 + mid + ['Y']) else: mid, dst = [], e.dst while not s[int(dst)] in p: mid.append(''.join([m.surface for m in s[int(dst)].morphs if m.pos != '記号'])) dst = s[int(dst)].dst ed = [''.join([m.surface for m in s[int(dst)].morphs if m.pos != '記号'])] pl.append([st1, st2 + mid, ed]) return pl filename = 'ch05/ai.ja.txt.cabocha' with open(filename, mode='rt', encoding='utf-8') as f: blocks = f.read().split('EOS\n') blocks = list(filter(lambda x: x != '', blocks)) blocks = [parse_cabocha(block) for block in blocks] for b in blocks: pl = (convert(b)) for p in pl: if isinstance(p[0], str): print(' -> '.join(p)) else: print(p[0][0], ' -> '.join(p[1]), p[2][0], sep=' | ')
言語処理100本ノック 2020「48. 名詞から根へのパスの抽出」
問題文
問題の概要
問題文に提示された仕様に従って出力します。
class Morph: def __init__(self, dc): self.surface = dc['surface'] self.base = dc['base'] self.pos = dc['pos'] self.pos1 = dc['pos1'] class Chunk: def __init__(self, morphs, dst): self.morphs = morphs # 形態素(Morphオブジェクト)のリスト self.dst = dst # 係り先文節インデックス番号 self.srcs = [] # 係り元文節インデックス番号のリスト def parse_cabocha(block): def check_create_chunk(tmp): if len(tmp) > 0: c = Chunk(tmp, dst) res.append(c) tmp = [] return tmp res = [] tmp = [] dst = None for line in block.split('\n'): if line == '': tmp = check_create_chunk(tmp) elif line[0] == '*': dst = line.split(' ')[2].rstrip('D') tmp = check_create_chunk(tmp) else: (surface, attr) = line.split('\t') attr = attr.split(',') lineDict = { 'surface': surface, 'base': attr[6], 'pos': attr[0], 'pos1': attr[1] } tmp.append(Morph(lineDict)) for i, r in enumerate(res): res[int(r.dst)].srcs.append(i) return res filename = 'ch05/ai.ja.txt.cabocha' with open(filename, mode='rt', encoding='utf-8') as f: blocks = f.read().split('EOS\n') blocks = list(filter(lambda x: x != '', blocks)) blocks = [parse_cabocha(block) for block in blocks] for b in blocks: for m in b: text = [] if '名詞' in [s.pos for s in m.morphs] and int(m.dst) != -1: current_chunk = m text.append(''.join([m.surface for m in current_chunk.morphs])) next_chunk = b[int(current_chunk.dst)] while int(current_chunk.dst) != -1: text.append(''.join([m.surface for m in next_chunk.morphs])) current_chunk = next_chunk next_chunk = b[int(next_chunk.dst)] print(*text, sep=' -> ')