AI 物見遊山 - 環境構築編 - 自然言語処理環境

環境構築編 - 自然言語処理環境

アイキャッチ画像

AI 物見遊山をするための自然言語処理用の環境を構築する。 前の記事 環境構築編 - 基礎環境 で作成した環境上で作業をする前提とする。

利用するパッケージ等

  • Transformers
    • Google が発表した深層学習モデル
    • https://huggingface.co/docs/transformers/ja/index
  • SpeechBrain
    • 音声処理ツールキット
    • https://speechbrain.github.io/
  • GiNZA NLP Library
    • 日本語の自然言語処理に特化したライブラリ
    • 形態素解析器
    • https://megagonlabs.github.io/ginza/
  • janome
    • 形態素解析器
    • https://mocobeta.github.io/janome/
  • MeCab
    • 形態素解析器
    • https://taku910.github.io/mecab/
    • mecab-python3
  • gensim
    • トピックモデリングや文書類似度分析に特化したライブラリ
    • https://radimrehurek.com/gensim/
  • pyannote.audio
    • 音声データから「いつ、誰が話したか」を自動的に推定する(話者ダイアライゼーション)ライブラリ
    • https://github.com/pyannote/pyannote-audio

構築手順

Transformers をインストールする。

pip install transformers[torch]

動作確認する。

python3 -c "from transformers import pipeline; print(pipeline('sentiment-analysis')('we love you'))"

自動的にモデルがダウンロードされ、以下のような表示がされる。

[{'label': 'POSITIVE', 'score': 0.9998704195022583}]

SpeechBrain をインストールする。

依存パッケージの ffmpeg をインストールする。 SpeechBrain は、処理内部で ffmpeg を利用している。

sudo apt install ffmpeg

SpeechBrain のインストール方法には、パッケージでインストールする方法と、ソースからインストールする方法がある。 パッケージでインストールすると、コア機能の一部のみ利用できる。 ソースからインストールすると、モデルの学習などからすべての機能が利用できる。

ここでは、ソースからインストールする。

git clone https://github.com/speechbrain/speechbrain.git
cd speechbrain
# torch-directml 0.2.5 の場合は、torch==2.5.0 以上だとエラーとなるため、バージョン制限をしている。
pip install -r requirements.txt 'torch>=1.9.0,<2.5.0'
pip install --editable .
pip install soundfile
pip install k2
sudo apt install build-essential
pip install ctc-segmentation
cd

動作確認する。

cat <<'EOL' | python
from speechbrain.inference import EncoderDecoderASR
asr_model = EncoderDecoderASR.from_hparams(source="speechbrain/asr-conformer-transformerlm-librispeech", savedir="pretrained_models/asr-transformer-transformerlm-librispeech")
print(asr_model.transcribe_file("speechbrain/tests/samples/ASR/spk1_snt1.wav"))
EOL

モデルのダウンロードされ、音声をテキストに変換する。 最後に以下のように変換されたテキストが表示される。

THE CHILD ALMOST HURT THE SMALL DOG

GiNZA NLP Library をインストールする。

GiNZA NLP Library は、オープンソース日本語 NLP ライブラリとなる。 spaCy を基盤とし、SudachiPy、Transformers などを利用してる。 形態素解析器としても利用できる。 自然言語処理モデルを利用するため、メモリ、CPU や、GPU などのリソースがかなり必要となる。

pip install -U ginza

GiNZA 用のモデルをインストールする。 モデルは、β版の ja_ginza_bert_large を利用する。

そのままインストールすると、依存パッケージ tokenizers v0.13.3 のインストールでエラーとなるため、依存パッケージ設定を修正する。 エラーは、tokenizers で、 casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell` のように発生する。 tokenizers は、spacy-transformers v1.2.5 の依存パッケージなので、spacy-transformers を v1.3.5 にすることでエラーを回避する。

以下のファイルをダウンロード

https://github.com/megagonlabs/ginza/releases/download/v5.2.0/ja_ginza_bert_large-5.2.0b1-py3-none-any.whl

whl ファイル(zipファイル)を解凍する。

sudo apt install zip unzip
mkdir work
cd work
unzip ../ja_ginza_bert_large-5.2.0b1-py3-none-any.whl

ja_ginza_bert_large-5.2.0b1.dist-info/METADATA ファイルを以下のように変更する。

vim ja_ginza_bert_large-5.2.0b1.dist-info/METADATA

12 行目
Requires-Dist: spacy-transformers <1.3.0,>=1.2.5
↓
Requires-Dist: spacy-transformers <1.4.0,>=1.2.5

再び zip 圧縮して、ファイル拡張子を .whl に変更する。

zip -r ja_ginza_bert_large-5.2.0b1-py3-none-any.whl ja_ginza_bert_large*

インストールする。

pip install ja_ginza_bert_large-5.2.0b1-py3-none-any.whl

動作確認

ginza コマンドで実行する。

echo "すももも、ももも、もものうち。" | ginza

途中、色々とログが出力され、最後に結果が出力される。

# text = すももも、ももも、もものうち。
1       すもも  すもも  NOUN    名詞-普通名詞-一_       4       nsubj   _       SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=SEM_HEAD|NP_B|Reading=スモモ|NE=B-OTHERS|ENE=B-Flora|ClauseHead=4
2       も      も      ADP     助詞-係 _詞     1       case    _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Reading=モ|ClauseHead=4
3       、      、      PUNCT   補助記号_読点   1       punct   _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=CONT|Reading=、|ClauseHead=4
4       もも    もも    NOUN    名詞-普通名詞-一_       9       nsubj   _       SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=SEM_HEAD|NP_B|Reading=モモ|NE=B-OTHERS|ENE=B-Flora|ClauseHead=4
5       も      も      ADP     助詞-係 _詞     4       case    _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Reading=モ|ClauseHead=4
6       、      、      PUNCT   補助記号_読点   4       punct   _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=CONT|Reading=、|ClauseHead=4
7       もも    もも    NOUN    名詞-普通名詞-一_       9       nmod    _       SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=SEM_HEAD|NP_B|Reading=モモ|NE=B-PRODUCT|ENE=B-Food_Other|ClauseHead=9
8       の      の      ADP     助詞-格 _詞     7       case    _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Reading=ノ|ClauseHead=9
9       うち    うち    NOUN    名詞-普通名詞-副_ 可能  0       root    _       SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=ROOT|NP_B|Reading=ウチ|ClauseHead=9
10      。      。      PUNCT   補助記号_句点   9       punct   _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=CONT|Reading=。|ClauseHead=9

Python で実行する。

cat <<'EOL' | python
import spacy
nlp = spacy.load('ja_ginza_bert_large')
doc = nlp('すももも、ももも、もものうち。')
for sent in doc.sents:
    for token in sent:
        print(
            token.i,
            token.orth_,
            token.lemma_,
            token.norm_,
            token.morph.get("Reading"),
            token.pos_,
            token.morph.get("Inflection"),
            token.tag_,
            token.dep_,
            token.head.i,
        )
    print('EOS')
EOL

ログの最後に以下のように表示される。 インデックスが 0 から始まっている点を除いて、コマンド実行時と結果は同じとなる。

0 すもも すもも 李 ['スモモ'] NOUN [] 名詞-普通名詞-一般 nsubj 3
1 も も も ['モ'] ADP [] 助詞-係助詞 case 0
2 、 、 、 ['、'] PUNCT [] 補助記号-読点 punct 0
3 もも もも もも ['モモ'] NOUN [] 名詞-普通名詞-一般 nsubj 8
4 も も も ['モ'] ADP [] 助詞-係助詞 case 3
5 、 、 、 ['、'] PUNCT [] 補助記号-読点 punct 3
6 もも もも もも ['モモ'] NOUN [] 名詞-普通名詞-一般 nmod 8
7 の の の ['ノ'] ADP [] 助詞-格助詞 case 6
8 うち うち うち ['ウチ'] NOUN [] 名詞-普通名詞-副詞可能 ROOT 8
9 。 。 。 ['。'] PUNCT [] 補助記号-句点 punct 8
EOS

janome をインストールする。

Janome は,Pure Python で書かれた、辞書内包の形態素解析器となる。 処理速度は、早くはない。

pip install janome

動作確認

cat <<'EOL' | python
from janome.tokenizer import Tokenizer
t = Tokenizer()
for token in t.tokenize('すももも、ももも、もものうち。'):
    print(token)
EOL

結果

すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
、      記号,読点,*,*,*,*,、,、,、
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
、      記号,読点,*,*,*,*,、,、,、
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
。      記号,句点,*,*,*,*,。,。,。

MeCab をインストールする。

MeCab は、C 言語で実装された高速な形態素解析器となる。 Python から利用するため、mecab-python3 パッケージを利用する。 辞書は、UniDic を利用する。

pip install mecab-python3 unidic
python -m unidic download

動作確認

cat <<'EOL' | python
import MeCab
stat = ['正常', '未知語', '文頭', '文末']
tagger = MeCab.Tagger()
node = tagger.parseToNode('すももも、ももも、もものうち。')
while node:
    print(f'表層形:"{node.surface}"')
    print(f'品詞:{node.feature}')
    print(f'状態:{stat[node.stat]}')
    print('-----')
    node = node.next
EOL

結果

表層形:""
品詞:BOS/EOS,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*
状態:文頭
-----
表層形:"すもも"
品詞:名詞,普通名詞,一般,*,*,*,スモモ,李,すもも,スモモ,すもも,スモモ,和,*,*,*,*,*,*,体,スモモ,スモモ,スモモ,スモモ,0,C2,*,15660352771596800,56972
状態:正常
-----
表層形:"も"
品詞:助詞,係助詞,*,*,*,*,モ,も,も,モ,も,モ,和,*,*,*,*,*,*,係助,モ,モ,モ,モ,*,"動詞%F2@-1,形容詞%F4@-2,名詞%F1",*,10324972564259328,37562
状態:正常
-----
表層形:"、"
品詞:補助記号,読点,*,*,*,*,*,、,、,*,、,*,記号,*,*,*,*,*,*,補助,*,*,*,*,*,*,*,6605693395456,24
状態:正常
-----
表層形:"もも"
品詞:名詞,普通名詞,一般,*,*,*,モモ,桃,もも,モモ,もも,モモ,和,*,*,*,*,*,*,体,モモ,モモ,モモ,モモ,0,C3,*,10425303000293888,37927
状態:正常
-----
表層形:"も"
品詞:助詞,係助詞,*,*,*,*,モ,も,も,モ,も,モ,和,*,*,*,*,*,*,係助,モ,モ,モ,モ,*,"動詞%F2@-1,形容詞%F4@-2,名詞%F1",*,10324972564259328,37562
状態:正常
-----
表層形:"、"
品詞:補助記号,読点,*,*,*,*,*,、,、,*,、,*,記号,*,*,*,*,*,*,補助,*,*,*,*,*,*,*,6605693395456,24
状態:正常
-----
表層形:"もも"
品詞:名詞,普通名詞,一般,*,*,*,モモ,桃,もも,モモ,もも,モモ,和,*,*,*,*,*,*,体,モモ,モモ,モモ,モモ,0,C3,*,10425303000293888,37927
状態:正常
-----
表層形:"の"
品詞:助詞,格助詞,*,*,*,*,ノ,の,の,ノ,の,ノ,和,*,*,*,*,*,*,格助,ノ,ノ,ノ,ノ,*,名詞%F1,*,7968444268028416,28989
状態:正常
-----
表層形:"うち"
品詞:名詞,普通名詞,副詞可能,*,*,*,ウチ,内,うち,ウチ,うち,ウチ,和,*,*,*,*,*,*,体,ウチ,ウチ,ウチ,ウチ,0,C3,*,881267193291264,3206
状態:正常
-----
表層形:"。"
品詞:補助記号,句点,*,*,*,*,*,。,。,*,。,*,記号,*,*,*,*,*,*,補助,*,*,*,*,*,*,*,6880571302400,25
状態:正常
-----
表層形:""
品詞:BOS/EOS,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*,*
状態:文末
-----

gensim をインストールする。

pip install POT gensim

Word2Vec で利用する学習済みモデルをインストール

chiVe: Sudachi による日本語単語ベクトル のデータを分散表現データとして利用する。 このサイトには、複数のデータがあり、先人が言うには「A 単位語のみ」の資源のデータで事足りそうだが、 大は小を兼ねるということで、全部入りのデータを利用することにする。

「データ」表「v1.3 mc5」の「gensim」欄の「2.9GB (tar.gz)」をダウンロードして、 ~/models/chive-1.3-mc5_gensim ディレクトリ以下にモデルファイルを格納する。

cd
mkdir models
cd models
wget https://sudachi.s3-ap-northeast-1.amazonaws.com/chive/chive-1.3-mc5_gensim.tar.gz
tar -zxvf chive-1.3-mc5_gensim.tar.gz

もう一つ分散表現データをインストールする。 GitHub - Wikipedia Entity Vectors サイトの リリースページから、 jawiki.all_vectors.300d.txt.bz2 ファイル(1.74 GB)をダウンロードする。 解凍した、jawiki.all_vectors.300d.txt ファイル(4.92 GB)を ~/models/ ディレクトリに格納する。

FastText で利用する学習済みモデルをインストール

以下のサイトにブラウザでアクセスする。

https://github.com/facebookresearch/fastText/blob/master/docs/crawl-vectors.md

モデルの Japanese: bin のリンクから日本語モデル圧縮ファイル(cc.ja.300.bin.gz)(4.18 GB)をダウンロードする。

https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ja.300.bin.gz

cc.ja.300.bin.gz ファイルを解凍して、cc.ja.300.bin ファイル(6.75 GB)を、 ~/models/ ディレクトリに格納する。

Doc2Vec で利用する学習済みモデルをインストール

学習済みモデル(dmpv300d)は、 日本語Wikipediaで学習したdoc2vecモデル - Out-of-the-box をありがたく利用させていただくつもりだったが、 このモデルは、gensim 3.x で作成されたようで (3.8.3 でロードできることは確認した)、 4.x では、ロード時に以下のようなエラーとなった。

AttributeError: 'Doc2Vec' object has no attribute 'dv'. Did you mean: 'dm'?

4.x 用に変換する方法などは見当たらなかったので、 諦めることにする。 モデルは、用途に合ったデータで学習させることが普通で、 汎用的なものは無いのかもしれない。 最初は、Word2Vec のモデルを、Doc2Vec に利用できるのかと思ったが、 そうではないようだ。

動作確認

Word2Vec のモデルを確認する。

cd
cat <<'EOL' | python
import gensim
model = gensim.models.KeyedVectors.load("./models/chive-1.3-mc5_gensim/chive-1.3-mc5.kv")
print(model.similarity('犬', '人'))
print(model.most_similar('徳島', topn=5))
EOL

の単語間の類似度と、徳島の単語の類似単語が表示される。

0.29249397
[('徳島県', 0.7955368757247925), ('愛媛', 0.7929633855819702), ('高知', 0.7896395325660706), ('徳島市', 0.769208
7888717651), ('香川', 0.7571912407875061)]

pyannote.audio をインストールする。

pyannote.audio 用のモデルを利用するために準備が必要となる。 事前に Hugging Face のアカウント(無料)を作成してログインする。

このパッケージ用のモデルを利用するには、アクセストークン生成などが必要となる。 モデルを利用する手順は、 github.com/pyannote/pyannote-audio を参照。

以下に、上記手順の補足を記載する。

  1. パッケージをインストールする。 pip install pyannote.audio
  2. pyannote/segmentation-3.0 利用条件に同意する
    1. pyannote/segmentation-3.0 にブラウザでアクセスする。
    2. You need to agree to share your contact information to access this modelCompany/universityWebsite を入力して、Agree and access repository を押下する。
  3. pyannote/speaker-diarization-3.1 利用条件に同意する
    1. pyannote/speaker-diarization-3.1 にブラウザでアクセスする。
    2. You need to agree to share your contact information to access this modelCompany/universityWebsite を入力して、Agree and access repository を押下する。
  4. アクセストークンを作成
    1. hf.co/settings/tokens にブラウザでアクセスする。
    2. + Create new token ボタンをクリックする。
    3. Token name 欄に任意の名前を入力する。
    4. Read access to contents of all public gated repos you can access をチェックする。
    5. Create token ボタンを押下する。
    6. 表示されたトークンを控えておく。再表示はできないので注意。

動作確認

単一の話者音声の場合

cat <<'EOL' | python
from pyannote.audio import Pipeline
pipeline = Pipeline.from_pretrained(
    "pyannote/speaker-diarization-3.1",
    use_auth_token="ここにトークンを指定する")
diarization = pipeline("speechbrain/tests/samples/ASR/spk1_snt1.wav")
for turn, _, speaker in diarization.itertracks(yield_label=True):
    print(f"start={turn.start:.1f}s stop={turn.end:.1f}s speaker_{speaker}")
EOL

ログの末尾に以下のように表示される。

start=0.0s stop=2.9s speaker_SPEAKER_00

複数の話者音声の場合

音声データを準備する

ffmpeg -i speechbrain/tests/samples/ASR/spk1_snt1.wav -i speechbrain/tests/samples/ASR/spk2_snt1.wav -filter_complex amix=inputs=2:duration=longest annote-sample.wav

# ファイルのフォーマットを確認する。
ffprobe -hide_banner annote-sample.wav

Input #0, wav, from 'annote-sample.wav':
  Metadata:
    encoder         : Lavf60.16.100
  Duration: 00:00:02.87, bitrate: 256 kb/s
  Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 16000 Hz, 1 channels, s16, 256 kb/s

# 再生する。
paplay annote-sample.wav

実行する。

cat <<'EOL' | python
from pyannote.audio import Pipeline
pipeline = Pipeline.from_pretrained(
    "pyannote/speaker-diarization-3.1",
    use_auth_token="ここにトークンを指定する")
diarization = pipeline("annote-sample.wav")
for turn, _, speaker in diarization.itertracks(yield_label=True):
    print(f"start={turn.start:.1f}s stop={turn.end:.1f}s speaker_{speaker}")
EOL

ログの末尾に以下のように表示される。

start=0.0s stop=1.9s speaker_SPEAKER_01
start=0.0s stop=2.9s speaker_SPEAKER_00