言語処理100本ノック第1章 解答と解説

初めまして。今年度からブレインズコンサルティングに入社した福田直起です。

私は今、Pythonを使った言語処理を学ぶために、言語処理100本ノック2020に取り組んでいます。
そのアウトプットとして、これからこのテックブログに回答と解説を発表していきます。
今回は「第1章 : 準備運動」の10問を解説していきます。

この記事のレベル感

Pythonについてif文やfor文などの基本的な使い方なら知っている初心者が、記事を通して言語処理におけるPythonの強みがある程度理解できるようになる」
というレベル感で解説していきます。

実行環境

  • Ubuntu 20.04.4 LTS
  • Jupyter Notebook 4.10.0
    • IPython 8.3.0
  • Python 3.8.13

問題を解くための前提知識

文字列の抽出

pythonでは文字列をリストのように扱うことができます。
これを利用すると、以下のように文字列の一部をリストのインデックスのように指定して、抽出することができます。

str = "abcde"
# 最初の文字を抽出
print(str[0])
# 実行結果:"a"

# -(マイナス)を指定すると、文字列を末尾から参照できる。最後の文字が-1
print(str[-1])
# 実行結果:"e"

参考: 組み込み型 — Python 3.8.10 ドキュメント

第1章の解答と解説

Q0. 文字列の逆順

文字列”stressed”の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.

解答コード

word = "stressed"
print(word[::-1])

解答コードの実行結果

desserts

解説

Pythonでは、リストに対するループ処理、すなわち文字列に対する連続した抽出処理を、
list[start:stop:step]

  • start: 開始位置
  • stop: 終了位置
  • step: 処理のステップ(何文字ごとに抽出するか)

のように書くことができます。

この問題では、stepに負の数である-1を指定することで、指定された文字列を末尾から順番に一文字ずつ抽出して並べています。

これを利用すれば、他にも以下のようなことができます。

str = "abcde"

# step部分は省略可(stepには1が自動的に指定される)
print(str[1:3])
# 実行結果:"bcd"

# startやstopも省略可
# startなら0、stopなら文字列の末尾が自動的に指定される
print(str[:3])
# 実行結果:"abc"

print(str[1:])
# 実行結果:"bcde"

# stepを指定して一文字飛ばしで文字列を抽出する例
print(str[1:4:2])
# 実行結果:"bd"

この書き方を使わずに、この問題をwhile文を使って解こうとすると、以下のようにかなり長いコードを書く必要があります。
言語処理にPythonを使う大きな強みと言える構文だと思います。

word = "stressed"

result = ""
count = len(word) - 1

while count >= 0:
    result += word[count]
    count -= 1
    
print(result)

参考: 組み込み型 — Python 3.8.10 ドキュメント

Q1. 「パタトクカシーー」

「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.

解答コード

word = "パタトクカシーー"

# 1,3,5,7文字目なので、1文字目から2文字ごとに取り出せばよい
print(word[::2])

解答コードの実行結果

パトカー

解説

指定されている文字の位置が2文字ごとであることから、一つ前の問題と同様、stepで1文字飛ばしを指定して必要な部分を抽出しています。

この問題をfor文を使って解いてみて、この構文の便利さを実感するのもいいかもしれません。

Q2. 「パトカー」+「タクシー」=「パタトクカシーー」

「パトカー」+「タクシー」の文字を先頭から交互に連結して文字列「パタトクカシーー」を得よ.

解答の前に

新しい用語が出てくるので、解答より前に解説しておきます。
marged_words = [char1 + char2 for char1, char2 in zip(word1, word2)]
解答でこのような式が出てきますが、この式の右辺のような書き方を「リスト内包表記」と言います。
詳しい使い方は、解答コードの後で解説します。

解答コード

word1 = "パトカー"
word2 = "タクシー"

# リスト内包表記を使って2つの単語を先頭から交互に結合する
marged_words = [char1 + char2 for char1, char2 in zip(word1, word2)]

# 結合したリストを出力用に文字列に変換する
result = "".join(marged_words)

print(result)

解答コードの実行結果

パタトクカシーー

解説

Python命名規則について

Pythonで変数名や関数名に複数の単語を使いたい場合、
marged_words
のようにアンダーバー区切りの小文字(スネークケースと呼ばれます)を使うのが一般的です。

zip()について

Pythonでは、
for item1, item2 in zip(list1, list2)
のように記述することで、一行のfor文で複数のリストを同時にループさせることができます。
もちろん、この問題の解答で実際に使用したように、文字列にも使えます。

参考: 組み込み関数 — Python 3.8.10 ドキュメント

リスト内包表記について

Pythonでは、リスト内包表記という書き方によって、一行でリストを定義することができます。
例として、以下のような場合に力を発揮します。

  1. この問題のように、zip()と併用して複数のリストを互い違いに結合する
  2. 単語のリストから、各単語の先頭の文字を抜き出す

リスト内包表記は、
list = [各項目に適用する式 for文]
のように書くことで使うことができます。

分かりやすいように、上の「2.単語のリストから、各単語の先頭の文字を抜き出す」をリスト内包表記を用いてコード化したものを、以下に例示しておきます。

コード
words = ["When", "Where", "Who", "What", "Why", "How"]

first_chars = [word[0] for word in words]

print(first_chars)
実行結果
['W', 'W', 'W', 'W', 'W', 'H']

参考: 組み込み型 — Python 3.8.10 ドキュメント

join()について

Pythonでは、
"".join(list)
のように記述することで、リストを文字列として結合することができます。

参考: 組み込み型 — Python 3.8.10 ドキュメント

リスト内包表記を使わない別解

リスト内包表記の便利さを実感してもらうために、以下にリスト内包表記を使わない別解を提示しておきます。

word1 = "パトカー"
word2 = "タクシー"

marged_words = []

for char1, char2 in zip(word1, word2):
    marged_words.append(char1 + char2)

result_str = "".join(marged_words)

print("リスト内包表記を使わない方法の結果:")
print(result_str)

一行で書く別解

今回は分かりやすさを重視して避けましたが、この問題はほぼ一行で解答することもできます。

word1 = "パトカー"
word2 = "タクシー"

print("リスト内包表記を使って一行で書く方法の結果:")
print("".join([char1 + char2 for char1, char2 in zip(word1, word2)]))

Q3. 円周率

“Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.”という文を単語に分解し,各単語の(アルファベットの)文字数を先頭から出現順に並べたリストを作成せよ.

解答コード

sentence = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."

# 文字数カウントにはカンマとピリオドが不要なので取り除く
sentence = sentence.replace(",", "").replace(".", "")

# 文を単語に分割する
splits = sentence.split()

ans = [len(split) for split in splits]
print(ans)

解答コードの実行結果

[3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]

解説

replace()について

Pythonでは、
str.replace("置換元の文字列", "置換後の文字列")
のように記述することで、変数str内の文字列を置換することができます。
今回は不要な文字を空文字に置換することで削除しています。

また、この構文は返り値が置換処理した後の文字列なので、解答コードのように
replace().replace()
とメソッドを一行に連続で書くことで、一行で複数の置換処理をさせることもできます。

参考: 組み込み型 — Python 3.8.10 ドキュメント

split()について

Pythonでは、
list = str.split("区切り文字")
のように記述することで、変数str文字列を区切り文字で区切ったリストに変換することができます。
区切り文字を指定しない場合、スペースなどの空白文字で区切られるため、今回はそれを利用しています。

参考: 組み込み型 — Python 3.8.10 ドキュメント

正規表現を用いる別解

今回はreplace()を使いましたが、正規表現を用いた別解を残しておきます。
正規表現については、第2章で詳しく扱う予定です。

import re

sentence = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics."

# 文字数カウントにはカンマとピリオドが不要なので取り除く
sentence = re.sub("[,.]", "", sentence)

# 文を単語に分割する
splits = sentence.split()

ans = [len(split) for split in splits]
print(ans)

Q4. 元素記号

“Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can.”という文を単語に分解し,1, 5, 6, 7, 8, 9, 15, 16, 19番目の単語は先頭の1文字,それ以外の単語は先頭に2文字を取り出し,取り出した文字列から単語の位置(先頭から何番目の単語か)への連想配列(辞書型もしくはマップ型)を作成せよ.

解答コード

sentence = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can."

# 文を単語に分割する
splits = sentence.split()

result = {}
special_numbers = [1, 5, 6, 7, 8, 9, 15, 16, 19]

# iの初期値を1に設定する
for i, split in enumerate(splits, 1):
    
    # 問題文で指定された番号の単語の際は先頭1文字
    if i in special_numbers:
        result[split[0]] = i
        
    # 問題文で指定されていない番号の単語の際は先頭2文字
    else:
        result[split[:2]] = i

print(result)

解答コードの実行結果

{'H': 1, 'He': 2, 'Li': 3, 'Be': 4, 'B': 5, 'C': 6, 'N': 7, 'O': 8, 'F': 9, 'Ne': 10, 'Na': 11, 'Mi': 12, 'Al': 13, 'Si': 14, 'P': 15, 'S': 16, 'Cl': 17, 'Ar': 18, 'K': 19, 'Ca': 20}

解説

enumerate()について

Pythonfor文のインデックス(現在位置)が必要な場合、
for インデックスを入れる変数名, リストの各アイテムを入れる変数名 in enumerate(list, インデックスの初期値):
のようにすればインデックスをループ内で使うことができます。
解答では、インデックスの初期値を1にしていますが、インデックスの初期値は省略可能で、その場合の初期値は0になります。

参考: 組み込み関数 — Python 3.8.10 ドキュメント

Q5. n-gram

与えられたシーケンス(文字列やリストなど)からn-gramを作る関数を作成せよ.この関数を用い,”I am an NLPer”という文から単語bi-gram,文字bi-gramを得よ.

n-gramとは?

解答を提示する前に、n-gramという新しい用語が出てきたので、それを解説します。

任意の文字列や文書を連続したn個の文字で分割するテキスト分割方法.特に,nが1の場合をユニグラム(uni-gram),2の場合をバイグラム(bi-gram),3の場合をトライグラム(tri-gram)と呼ぶ.

引用元:コトバンク

定義だけだとわかりにくいので、
I work in BCI
という文章をN-gramにして例示します。

uni-gram(N=1)

文字:
[('I',), (' ',), ('w',), ('o',), ('r',), ('k',), (' ',), ('i',), ('n',), (' ',), ('B',), ('C',), ('I',)]

単語:
[('I',), ('work',), ('in',), ('BCI',)]

bi-gram(N=2)

文字:
[('I', ' '), (' ', 'w'), ('w', 'o'), ('o', 'r'), ('r', 'k'), ('k', ' '), (' ', 'i'), ('i', 'n'), ('n', ' '), (' ', 'B'), ('B', 'C'), ('C', 'I')]

単語:
[('I', 'work'), ('work', 'in'), ('in', 'BCI')]

tri-gram(N=3)

文字:
[('I', ' ', 'w'), (' ', 'w', 'o'), ('w', 'o', 'r'), ('o', 'r', 'k'), ('r', 'k', ' '), ('k', ' ', 'i'), (' ', 'i', 'n'), ('i', 'n', ' '), ('n', ' ', 'B'), (' ', 'B', 'C'), ('B', 'C', 'I')]

単語:
[('I', 'work', 'in'), ('work', 'in', 'BCI')]

N-gramについて理解していただけたでしょうか。
それでは、解答と解説に入ります。

解答コード

# 以降の問題でも使用する
def n_gram(target: list, n: int) -> list:
    result = []
    
    # 最後の文字や単語の(1-n)つ前までループを回す
    for i, item in enumerate(target[:len(target) + 1 - n]):
        # n-gramの一部として、連続したn個の文字のリストを作成する
        list = target[i : i + n]
        
        # 結果表示の際に見やすいのでタプルに変換する
        result.append(tuple(list))
    
    return result

target = "I am an NLPer"

print("文字bi-gram:")
print(n_gram(target, 2))
print()

# 単語ごとのリストに分割
words = target.split()

print("単語bi-gram:")
print(n_gram(words, 2))

解答コードの実行結果

文字bi-gram:
[('I', ' '), (' ', 'a'), ('a', 'm'), ('m', ' '), (' ', 'a'), ('a', 'n'), ('n', ' '), (' ', 'N'), ('N', 'L'), ('L', 'P'), ('P', 'e'), ('e', 'r')]

単語bi-gram:
[('I', 'am'), ('am', 'an'), ('an', 'NLPer')]

解説

Q1で解説したとおり、文字列をリストとして扱えるというPythonの特徴と、list[start:stop]を使うことで、「最後の文字や単語の(1-n)つ前までループを回す」という処理を簡潔に書くことができています。

while文を使った別解

この問題で指定されている関数はwhile文を使って定義することもできます。

def n_gram(target: str, n: int) -> list:
    result = []
    count = 0
    
    while count + n - 1 < len(target):
        # n-gramの一部として、連続したn個の文字のリストを作成する
        list = target[count : count + n]
        
        # 結果表示の際に見やすいのでタプルに変換する
        result.append(tuple(list))
        
        count += 1
    
    return result

Q6. 集合

paraparaparadise”と”paragraph”に含まれる文字bi-gramの集合を,それぞれ, XとYとして求め,XとYの和集合,積集合,差集合を求めよ.さらに,’se’というbi-gramがXおよびYに含まれるかどうかを調べよ.

解答コード

target1 = "paraparaparadise"
target2 = "paragraph"

# Q5で作った関数を使用する
X = set(n_gram(target1, 2))
Y = set(n_gram(target2, 2))

print("X:")
print(X)
print()

print("Y:")
print(Y)
print()


print("XとYの和集合:")
print(X | Y)
print()

print("XとYの積集合:")
intersection = X & Y
print(intersection)
print()

print("XとYの差集合:")
print(X - Y)
print()

print("XおよびYに’se’というbi-gramが含まれるか:")
print("se" in intersection)

解答コードの実行結果

X:
{('r', 'a'), ('d', 'i'), ('p', 'a'), ('s', 'e'), ('a', 'p'), ('a', 'r'), ('a', 'd'), ('i', 's')}

Y:
{('r', 'a'), ('a', 'g'), ('g', 'r'), ('p', 'a'), ('p', 'h'), ('a', 'p'), ('a', 'r')}

XとYの和集合:
{('r', 'a'), ('a', 'g'), ('g', 'r'), ('d', 'i'), ('p', 'a'), ('s', 'e'), ('p', 'h'), ('a', 'p'), ('a', 'r'), ('a', 'd'), ('i', 's')}

XとYの積集合:
{('a', 'p'), ('r', 'a'), ('a', 'r'), ('p', 'a')}

XとYの差集合:
{('d', 'i'), ('a', 'd'), ('s', 'e'), ('i', 's')}

XおよびYに’se’というbi-gramが含まれるか:
False

解説

問題文の「XおよびY」という言葉が少し曖昧で、「XとYの積集合」と「XとYの和集合」のどちらともとれるように感じました。
ここでは、「XとYの和集合」なら「XおよびY」ではなく「XまたはY」や「XかY」と書くだろうという理由から、「XとYの積集合」と解釈して解いています。

Pythonsetについて

Pythonではsetを使うことで、集合を定義することができます。
setは順番を保持しませんが、同じ項目を重複して持たないので、listより便利な場合があります。
example_set = set(example_list)
とすることで、リストを集合に変換できます。

またPythonでは、解説コードのように和集合や積集合なども簡単に求めることができます。

この問題では使っていませんが、集合を宣言する場合は
empty_set = set()
のように宣言する必要があります。
リストを
empty_list = []
と宣言するように
empty_set = {}
と宣言してしまうと、集合ではなく辞書型の宣言になってしまうので注意が必要です。

参考: 組み込み関数 — Python 3.8.10 ドキュメント

Pythonで変数などを宣言する際の注意

Pythonで変数やsetlistを宣言する際に、気をつけたいことがあります。
例があったほうがわかりやすいので、以下にサンプルコードを提示します。

list = []
set = list(list)
# 以下のようなエラーが出る
# TypeError: 'list' object is not callable

PythonJavaなどに比べると、予約語(そのままで変数名にされてしまうと、プログラムがうまく処理できなくなるので、エラーになる語)の範囲が狭めに設定されており、setlistといった語をそのまま変数名にすることができてしまいます。
しかし、これらはset()list()といった組み込みメソッドで使う語であり、これらの語をそのまま変数名にしてしまうと、組み込みメソッドの方が使えなくなってしまいます。
エラー内容からミスした内容ががわかりにくいタイプのエラーの出方をするので、気をつけてください。

Pythonの組み込みメソッド(組み込み関数)について知りたい方は、以下の公式ドキュメントの該当ページを参考にしてください。
組み込み関数 — Python 3.8.10 ドキュメント

また、予約語について詳しく知りたい方は、以下のサイトを参考にしてください。
Javaを例に出したので、そちらの予約語も載せておきます。
Pythonのキーワード(予約語)一覧を確認するkeyword | note.nkmk.me
Java | Javaの予約語

Q7. テンプレートによる文生成

引数x, y, zを受け取り「x時のyはz」という文字列を返す関数を実装せよ.さらに,x=12, y=”気温”, z=22.4として,実行結果を確認せよ.

解答コード

def time_syntax(x: str, y: str, z: str) -> str:
    return f'{x}時の{y}は{z}'

print(time_syntax(12, "気温", 22.4))

解答コードの実行結果

12時の気温は22.4

解説

Pythonf文字列について

簡素な記法で変数入りの文字列をフォーマットできるため、この問題のように変数混じりの文字列を使いたい場合に便利です。
以下のように、変数名を{}でくくって使います。
f'私の名前は{name}です。'

比較的最近(Python3.6以降)でのみ使用可能なので、古い環境では使えないことには注意が必要です。

参考: 7. 入力と出力 — Python 3.8.10 ドキュメント

Q8. 暗号文

与えられた文字列の各文字を,以下の仕様で変換する関数cipherを実装せよ.
・英小文字ならば(219 - 文字コード)の文字に置換
・その他の文字はそのまま出力

この関数を用い,英語のメッセージを暗号化・復号化せよ.

文字コードとは

文字コードという単語が初めて出てきたので、解説しておきます。

コンピュータ上では、文字はデジタル符号で表される。この符号を文字コードという。キャラクターコードともいう。アルファベットのように字種の数が少ない場合は符号として必要なビット数が少なくてすみ、1バイト(8ビット)が1文字に割り当てられているが、字種の多い漢字を使う日本語などでは1文字に数バイト必要である。 引用元:コトバンク

つまり文字コードは数値なので、この問題では文字列の暗号化のために、文字を一旦文字コード化して引き算しようとしているわけですね。

文字コードは複数の種類がありますが、この問題ではPythonで扱いやすいUnicodeが入力されることを想定して解答します。

解答コード

def cipher(target: str) -> list:
    encrypted_list = []
    
    # for文で処理するために入力文字列をリスト化
    target_list = list(target)
    
    for target in target_list:
        
        # 英小文字ならば(219 - 文字コード)の文字に置換
        if target.islower():
            encrypted_list.append(chr(219 - ord(target)))
        
        # その他の文字はそのまま出力
        else:
            encrypted_list.append(target)
    
    return "".join(encrypted_list)

target1 = "para2para1para3dise"
print("入力文:")
print(target1)

encrypted_target1 = cipher(target1)
print()
print("暗号化した文:")
print(encrypted_target1)

print()
print("復号化した文:")
print(cipher(encrypted_target1))

解答コードの実行結果

入力文:
para2para1para3dise

暗号化した文:
kziz2kziz1kziz3wrhv

復号化した文:
para2para1para3dise

解説

Pythonにおける大文字・小文字の判定

Pythonには、以下のように標準で大文字・小文字を判定するメソッドがあります。

# 大文字かを判定
str1 = "A"
print(str1.isupper())
# 実行結果:"True"

# 小文字かを判定
str2 = "a"
print(str2.islower())
# 実行結果:"True"

Pythonでの文字コードの使い方

Pythonでは、
ord("文字コードに変換したい文字")
とすることで、文字をUnicode文字コードに変換できます。

参考: 組み込み関数 — Python 3.8.10 ドキュメント

逆に、Unicode文字コードを文字列に戻したい場合は
chr(文字コード)
を使います。

参考: 組み込み関数 — Python 3.8.10 ドキュメント

Q9. Typoglycemia

スペースで区切られた単語列に対して,各単語の先頭と末尾の文字は残し,それ以外の文字の順序をランダムに並び替えるプログラムを作成せよ.ただし,長さが4以下の単語は並び替えないこととする.

適当な英語の文(例えば”I couldn’t believe that I could actually understand what I was reading : the phenomenal power of the human mind .”)を与え,その実行結果を確認せよ.

解答コード

import random

# 結果が毎回同じになるように、乱数シードを固定する
random.seed(314) 

target = "I couldn’t believe that I could actually understand what I was reading : the phenomenal power of the human mind ."

# 文章を単語に分割する
splits = target.split()
result_list = []

for split in splits:
    # 長さが4を超える単語のみ順序をランダムに並び替える
    if len(split) >= 5:
        
        # 並び替え対象の部分をリスト化
        randomize_target_split = split[1:-1]
        
        # ランダムに並び替える
        randomized_split = random.sample(randomize_target_split, len(randomize_target_split))
        
        # 先頭や末尾と結合する
        result_list.append(split[0] + "".join(randomized_split) + split[-1])

    else:
        result_list.append(split)

result = " ".join(result_list)
print(result)

解答コードの実行結果

I cudonl’t bleevie that I colud actlaluy urdetsnnad what I was rineadg : the pamnneohel pewor of the hamun mind .

解説

Pythonrandomライブラリの使い方

Pythonでランダムな処理を行いたい場合に使うライブラリです。 以下に、解答コードで使用したものを中心に、コードを例示しながらrandomライブラリのメソッドについて解説します。

import random

# 結果が毎回同じになるように、乱数シードを固定するrandom.seed()
random.seed(314) 

example_list = [1, 2, 3, 4, 5, 6, 7]

# 重複なしでリストから要素を取り出すrandom.sample()
print(random.sample(example_list, len(example_list)))
# 結果:[2, 4, 1, 7, 5, 6, 3]

# 重複ありでリストから要素を取り出すrandom.choices()
print(random.choices(example_list, k=len(example_list)))
# 結果:[3, 3, 7, 1, 5, 1, 6]
# random.sample()と違い、重複ありなので、同じ要素が複数回取り出される

コードの再現性を担保するために、random.seed()で結果を固定しています。

参考: random --- 擬似乱数を生成する — Python 3.8.10 ドキュメント

join()で区切り文字を入れる

Q2でも解説したjoin()ですが、区切り文字を入れてリストを文字列として結合することもできます。
"区切り文字".join(list)

参考: 組み込み型 — Python 3.8.10 ドキュメント

Google Colaboratoryによる解答の公開

Google Colaboratoryを利用してJupyter Notebookを共有する形式でも解答を公開しています。 https://colab.research.google.com/drive/1QbWjJJgoeL53udr3gKtOV0_NZsXDR9cb?usp=sharing

おわりに

以上、言語処理100本ノックの第1章について解説しました。
私が本格的にPythonに取り組んだのは今回の言語処理100本ノックが初めてだったのですが、Pythonで言語処理をする際に便利な記法を多数学ぶことができたと感じています。 この章で解説した技法はのちの章でも多用するので、しっかりと覚えておくとスムーズに解けると思います。

あの論文を検証してみた! - シリーズ第6回 - Structual Probe 論文

こんにちは!ブレインズコンサルティングの大下です。

今回は、「あの論文を検証してみた!」のシリーズ第6回、A Structural Probe for Finding Syntax in Word Representationsを検証していきます。 BERT、ELMo で埋め込んだ空間表現の中に、パースツリー情報が埋め込まれているらしいことを示した論文です。 本当に埋め込まれていると言ってよいのか、具体的にどのようにして構造を復元しているのかが疑問になるところです。

続きを読む

あの論文を検証してみた! - シリーズ第5回 - Neural Processes 論文(実験編)

こんにちは!ブレインズコンサルティングの大下です。

今回は、「あの論文を検証してみた!」のシリーズ第5回、前回理論的(数学的・確率論的)な側面を解説したNeural Processes の論文の実験編です。 DeepMind社のTensorflow による実装を参考に、PyTorch 版を作成しました。 意外と、DeepMind社の実装のように、ある程度抽象化したりモジュール構成を整理したPyTorch版Neural Processesの実装がない印象だったので、独自作成しました。

今回の実験編では、Few-Shot Leaning をテーマに、いくつかのデータセット(※)で検証していきます。

(※)使ったデータセットは、Toy Dataset、MNIST、Fashion-MNIST、Kuzushiji-MNIST です。

続きを読む

あの論文を検証してみた! - シリーズ第4回 - Neural Processes 論文(解説編)

こんにちは!ブレインズコンサルティングの大下です。

今回は、「あの論文を検証してみた!」のシリーズ第4回、Neural Processes の論文について解説します。 Neural Processes の論文を選んだモチベーションは、もともと確率とニューラルネットワークの合わせ技のモデルに興味があったところ、 Deepmind 社が、ICMLでガウス過程の深層学習版として提案した当論文が目に入って来たというところです。

続きを読む

あの論文を検証してみた! - シリーズ第3回 - Neural ODE 論文

こんにちは!ブレインズコンサルティングの大下です。

今回は、「あの論文を検証してみた!」のシリーズ第3回、Neural ODE の論文について解説、検証します。 今回の論文は、Neural Ordinary Differential Equationsで、ResNet と、オイラー法の更新則の類似性に着目し、 連続時間のモデルへ拡張した、新しい考え方、手法を提案した論文です。 個人的には、かなり内容が濃い論文の印象で、勉強になった論文です。

続きを読む

あの論文を検証してみた! - シリーズ第2回 - BERT 論文(実験編)

こんにちは!ブレインズコンサルティングの大下です。

今回は、「あの論文を検証してみた!」のシリーズ第2回、BERTの可視化実験を紹介します。 BERTの枠組みで学習したTransformer (Self-Attention) が、入力系列のどこを注目しているのか、を可視化し、解釈を試みます。

先月ぐらいに、政府が考えるAIの7原則が記事になっていました。 その中に、「企業に決定過程の説明責任」というものがあり、一部で話題になっていたと記憶しています(批判が多かった印象)。 日本の戦略を考えると、量で質をカバーする方法では、もはや米国、中国には叶わないということもありそうなので、 仮に少量でも、日本らしい?質(==説明責任による安心・安全)を担保して差異化を図りたい、という流れになるのかもしれません。

ということで、説明責任に繋がるといいなぁという願いを込めて、BERTのアテンションの可視化に挑戦します!

続きを読む

あの論文を検証してみた! - シリーズ第1回 - BERT 論文(解説編)

はじめまして、ブレインズコンサルティングの大下です。

ブレインズコンサルティングでは、過去Blogger で、技術的な情報を公開していましたが、長らく更新が途絶えていたこともあり、 そちらを廃止し、こちらで、新たなテックブログとして開始することになりました。

記念すべき初回記事は、「あの論文を検証してみた!」のシリーズ第1回、今(2018年11月)、話題沸騰中(?)の 論文 [1810.04805] BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding の解説です!

なにやら、複数の自然言語処理タスクでSOTAをたたき出して、すごいらしいということは、各種記事により、すぐわかったのですが、具体的にどういう仕組みですごいことができているのか、よくわからなかったので、「論文とGitHub のコードから探ってみよう!」というのが本記事執筆のモチベーションになっています。

そこで、本記事では、論文を読んで、実際に GitHub のコード (GitHub - google-research/bert: TensorFlow code and pre-trained models for BERT) を確認した結果を共有します。

続きを読む