前々回の記事でTensorFlowを使ってテキスト分類をした結果、あまりうまくいきませんでした。原因として助詞や助動詞といった、テキストを分類する上であまり意味のなさそうなものがあるからかなと感じました。
そこで今回は以前作ったJapaneseTextEncoderに指定した品詞を取り除く機能を追加したうえで、もう一度TensorFlowを使ったテキスト分類をやってみます。
関連リンク
TensorFlowを使ってテキスト分類をしてみる。 - どん底から這い上がるまでの記録
日本語のテキストコーパスから辞書を作るライブラリを作りたい - どん底から這い上がるまでの記録
JapaneseTextEncoderの改良
この前作ったJapaneseTextEncoderはMeCabのオプションに”-Owakati"を指定していました。品詞を取り除くにはオプションを”-Ochasen"に変更します。
self.tagger = MeCab.Tagger("-Ochasen")
また、テキストを形態素に分割するメソッドtokenize
を以下のように変更します。
def tokenize(self, sentence):
return self.tagger.parse(sentence).strip().split()
def tokenize(self, sentence):
tag = self.tagger.parseToNode(sentence)
tokens = []
while tag:
features = tag.feature.split(",")
pos = features[0]
token = tag.surface
if pos in self.filters:
tag = tag.next
continue
tokens.append(token)
tag = tag.next
return tokens if tokens else None
tokenizeには変更前と同じように文字列を渡します。戻り値は要素が形態素のリストを返します。self.filters
には除外する品詞が要素になったリストになっています。
e.g. self.filters = ["助詞", "助動詞"]
改良したところは以上です。
TensorFlowを使ったテキスト分類
filtering
今回は助詞と助動詞をテキストから取り除くので、以前書いたdatasets.pyを以下のように変更します。
encoder = JapaneseTextEncoder(
sentences,
min_occurrences=1,
filters=["助詞", "助動詞"],
append_eos=False
)
学習
他に変更点はないので、前に書いたプログラムで学習をします。
結果
results = model.evaluate(test_x, test_t)
print(results)
分類精度は75%となりました。前は73%だったので、2%上がりましたね。
[0.5709238412396219, 0.7516778555492427]
Epochを増やす
助詞と助動詞を取り除く前処理をしただけでは前と結果は変わりませんでした。が、分類精度が悪いのはただ単にepoch数が短いからなのでは、と思ったのでepoch数を40から100に前処理をしないで再度学習をしてみました。
結果
results = model.evaluate(test_x, test_t)
print(results)
結果は、、、
[0.26587492907607313, 0.9127516786524114]


結論
今回のケースは前処理が問題ではなく、エポック数が少ないのが原因でした。学習するために使ったデータセットによっては、助詞や助動詞を取り除くといった前処理が必要になると思いたい。
ゼロから作るDeepLearning
自然言語処理を勉強したい方へのおすすめの本です。
tensorflow, chainerやPyTorchといったフレームワークを使わずにゼロからnumpyを使ってディープラーニングの実装をしています。
扱っている内容はword2vec, RNN, GRU, seq2seqやAttentionなど、、、
text_encoder.py
from collections import Counter
from reserved_tokens import SOS_INDEX
from reserved_tokens import EOS_INDEX
from reserved_tokens import UNKNOWN_INDEX
from reserved_tokens import RESERVED_ITOS
class JapaneseTextEncoder:
""" Encodes the text using a tokenizer.
Args:
corpus (list of strings): Text strings to build dictionary on.
min_occurrences (int, optional): Minimum number of occurences for a token to be
added to dictionary.
append_sos (bool, optional): If 'True' append SOS token onto the begin to the encoded vector.
append_eos (bool, optional): If 'True' append EOS token onto the end to the encoded vector.
reserved_tokens (list of str, optional): Tokens added to dictionary; reserving the first
'len(reserved_tokens') indices.
Example:
>>> corpus = ["セネガルつええ、ボルト三体くらいいるわ笑笑", \
"しょーみコロンビアより強い", \
"それなまちがいないわ"]
>>> encoder = JapaneseTextEncoder(corpus)
>>> encoder.encode("コロンビア強い")
[18, 20, 2]
>>> encoder.vocab
['<pad>', '<unk>', '</s>', '<s>', 'セネガル', 'つえ', 'え', '、', 'ボルト', '三', '体', 'くらい', ' いる', 'わ', '笑', 'しょ', 'ー', 'み', 'コロンビア', 'より', '強い', 'それ', 'な', 'まちがい', 'ない']
>>> encoder.decode(encoder.encode("コロンビア強い"))
コロンビア強い</s>
"""
def __init__(self,
corpus,
min_occurrences=1,
append_sos=False,
append_eos=True,
filters=None,
reserved_tokens=RESERVED_ITOS):
try:
import MeCab
except ImportError:
print("Please install MeCab.")
raise
if not isinstance(corpus, list):
raise TypeError("Corpus must be a list of strings.")
self.tagger = MeCab.Tagger("-Ochasen")
self.append_sos = append_sos
self.append_eos = append_eos
self.tokens = Counter()
self.filters = ["BOS/EOS"]
if filters is not None:
if not isinstance(filters, list):
raise TypeError("Filters must be a list of POS.")
self.filters += filters
for sentence in corpus:
tokens = self.tokenize(sentence)
if tokens:
self.tokens.update(tokens)
self.itos = reserved_tokens.copy()
self.stoi = {token: index for index, token in enumerate(reserved_tokens)}
for token, cnt in self.tokens.items():
if cnt >= min_occurrences:
self.itos.append(token)
self.stoi[token] = len(self.itos) - 1
@property
def vocab(self):
return self.itos
@property
def word2id(self):
return self.stoi
@property
def id2word(self):
return {index: token for token, index in self.stoi.items()}
def encode(self, sentence, sos_index=SOS_INDEX, eos_index=EOS_INDEX, unknown_index=UNKNOWN_INDEX):
tokens = self.tokenize(sentence)
if tokens is None:
raise TypeError("Invalid type None...")
indices = [self.stoi.get(token, unknown_index) for token in tokens]
if self.append_sos:
indices.insert(0, sos_index)
if self.append_eos:
indices.append(eos_index)
return indices
def decode(self, indices):
tokens = [self.itos[index] for index in indices]
return "".join(tokens)
def tokenize(self, sentence):
tag = self.tagger.parseToNode(sentence)
tokens = []
while tag:
features = tag.feature.split(",")
pos = features[0]
token = tag.surface
if pos in self.filters:
tag = tag.next
continue
tokens.append(token)
tag = tag.next
return tokens if tokens else None