物書き界隈を追い出されたので異世界で形態素解析することにしました

## はじめに

最近、小説投稿サイトでライトノベルを書いていて、コードを書いていませんでした。生成系AIが一般化したので、もともと文系の私にとっては、小説の方が自分で書く意味を感じられます。ということで、Xの相互フォロワー様はほとんど小説投稿サイトの方々です（※別に追放されてません）。

ある時、界隈で擬人法（personification）の修辞的技巧について会話することがありました。人物に適用する形容詞を物質に適用するなどの技法が一般的です。ふと、テキストをマイニングして形容詞と名詞を抽出しランダムに組み合わせると、新たな擬人化の発想が出てくるのではないかと思い、それを試してみることにしました。

今回はjanomeとGiNZAを使ってみます。門外漢のため（※転生してきたので）、誤解などあるかもしれませんが、ご容赦ください。

## 環境
Windows11 64bit
Python 3.10.11
janome 0.5.0
GiNZA 5.2
```
$ pip install janome
$ pip install ja-ginza
```

## janome版

自分の小説を実行ディレクトリにsample.txtとして置き、その内部のテキストから形容詞と名詞を抽出してランダムに組み合わせます。ソースコードは以下の通りです。

```Python
from janome.tokenizer import Tokenizer
import random

def read_text(file_path):
    # ファイルからテキストを読み込む
    with open(file_path, 'r', encoding='utf-8') as file:
        text = file.read().replace('\n', '').replace('　', '')
    return text

def generate_combinations(text):
    tokenizer = Tokenizer()
    adjectives = []  # 形容詞を保存するリスト
    nouns = []  # 名詞を保存するリスト

    # テキストを形態素解析して品詞を抽出
    for token in tokenizer.tokenize(text):
        part_of_speech = token.part_of_speech.split(',')[0]
        if part_of_speech == '形容詞':
            # 形容詞の原形を保存
            adjective = token.base_form if token.base_form != '*' else token.surface
            adjectives.append(adjective)
        elif part_of_speech == '名詞':
            # 名詞の原形を保存
            noun = token.base_form if token.base_form != '*' else token.surface
            nouns.append(noun)

    # 形容詞と名詞の組み合わせをランダムに生成
    combinations = []
    random.shuffle(adjectives)
    random.shuffle(nouns)

    for adjective, noun in zip(adjectives, nouns):
        if adjective.endswith('い') or adjective.endswith('な'):
            combination = f'{adjective}{noun}'  # 「い」または「な」で終わる場合、直接連結
        else:
            combination = f'{adjective}な{noun}'  # その他の形容詞は「な」を使用して連結
        combinations.append(combination)

    return combinations

# 使用例
file_path = 'sample.txt'
text = read_text(file_path)
combinations = generate_combinations(text)

# 組み合わせの出力
for combo in combinations:
    print(combo)
```
形容詞には、「い」で終わるものと「な」で終わるものがあり、それ以外は「な」を使って名詞と連結することにしました。また、小説では全角スペースで字下げを行うことが多いので、それを取り除く処理も入れています。

結果は以下の通り。
```
ない気持ち
美しい心理
遠い入れ
良い側
ない鏡
低い凝固
良いそれ
高い部屋
美しい天井
暖かい様
恐ろしい円
儚い両目
多いよう
快い経験
悲しいどこ
温かいの
無い枕
ない店
淡い確か
美しい以前
懐かしい溝
ない乍
暖かい僕
深い後ろ
```
Pythonのみでシンプルに導入できたのですが、何度か実行すると、「の」などの判定は少し異なる気がします。
次に、GiNZAも試してみることにします。

## GiNZA版

同様の処理をGiNZAでも書いてみます。

```Python
import spacy
import random

# GiNZAのロード
nlp = spacy.load('ja_ginza')

def read_text(file_path):
    # ファイルからテキストを読み込む
    with open(file_path, 'r', encoding='utf-8') as file:
        text = file.read().replace('\n', '').replace('　', '')
    return text

def generate_combinations(text):
    doc = nlp(text)
    adjectives = []  # 形容詞を保存するリスト
    nouns = []  # 名詞を保存するリスト

    # テキストを形態素解析して品詞を抽出
    for token in doc:
        if token.pos_ == 'ADJ':  # 形容詞
            adjectives.append(token.lemma_)  # 原形を使用
        elif token.pos_ == 'NOUN':  # 名詞
            nouns.append(token.lemma_)  # 原形を使用

    # 形容詞と名詞の組み合わせをランダムに生成
    combinations = []
    random.shuffle(adjectives)
    random.shuffle(nouns)

    for adjective, noun in zip(adjectives, nouns):
        if adjective.endswith('い') or adjective.endswith('な'):
            combination = f'{adjective}{noun}'  # 「い」または「な」で終わる場合、直接連結
        else:
            combination = f'{adjective}な{noun}'  # その他の形容詞は「な」を使用して連結
        combinations.append(combination)

    return combinations

# 使用例
file_path = 'sample.txt'
text = read_text(file_path)
combinations = generate_combinations(text)

# 組み合わせの出力
for combo in combinations:
    print(combo)
```

結果は以下の通り
```
小さな顔
ない光
低い女
同じな顔
迅速な鏡
美しい言葉
確かな口
正確な世界
明晰な段ボール
多い反復
温かい首
美しいプラットホーム
駄目な辺り
恐ろしい暗がり
快い心理学
暗い事
確かな毎朝
暖かい右横
悲しい視界
淡い街並み
曖昧な庭
深い話
無い言葉
微かなオレンジ
美しい場所
ない夢
良い自分
遠いこと
```
ランダムに実行しているため、1サンプルだけで評価はできませんが、少し自然になった気がします。GiNZAは依存構造や類似度の解析もできるようなので、依存パッケージも多く、計算資源も多く用いると思われます。ただ、今回のソーステキストはそこまで長いものではなく、パフォーマンス測定はしていません。

今回はここまでです。なお、取り込んだテキストは以下の短文でした（宣伝）。

https://novelup.plus/story/675980146/410633318