長い文書を自然に分割するプログラム

長いテキストを指定した最大文字数（デフォルトでは25文字）に基づいてセグメントに分割するためのものです。特に、日本語と中国語のテキストに対応しています。

1. **初期設定**: `file_path`で指定したテキストファイルのパスをセットします。
2. **モデルの初期化**: 日本語のトークナイザーとして`Janome`を、中国語の分かち書きと品詞タグ付けのために`jieba`を、そしてセグメンテーションの最適化にはBERTモデルを使用します。
3. **言語の検出**: 与えられたテキストの言語を判定します。日本語か中国語かを判定するための簡単な方法を使用しています。
4. **中国語のセグメンテーション**: `jieba`を使って中国語のテキストを分割します。動詞と目的語の間を分割しないようにしています。
5. **日本語のセグメンテーション**: `Janome`を使用して、日本語のテキストを分割します。
6. **BERTモデルによる最適化**: BERTモデルを使用してセグメンテーションの最適な分割点を見つけ、自然な分割を行います。
7. **実行**: `file_path`で指定したテキストファイルから内容を読み込み、上記の手順でセグメンテーションを行った結果を出力します。

このプログラムを使用することで、日本語や中国語のテキストを自然にセグメントに分割することができます。

```

# ファイルのパス設定
file_path = "/home/user/text.txt"

from janome.tokenizer import Tokenizer as JanomeTokenizer
import jieba.posseg as pseg
from transformers import BertTokenizer, BertForMaskedLM
import torch

class TextSegmentation:
    def __init__(self, max_length=25, model_name="bert-base-multilingual-cased"):
        self.max_length = max_length

        # Initialize models
        self.janome = JanomeTokenizer()
        jieba.initialize()
        self.bert_tokenizer = BertTokenizer.from_pretrained(model_name)
        self.bert_model = BertForMaskedLM.from_pretrained(model_name).eval()

    def detect_language(self, text):
        if any('\u4e00' <= ch <= '\u9fff' for ch in text):
            return 'zh'
        elif any('\u3040' <= ch <= '\u30ff' for ch in text):
            return 'ja'
        return 'unknown'

    def segment_chinese(self, text):
        words = list(pseg.cut(text))
        buffer, result = "", []

        for word, flag in words:
            buffer += word
            if len(buffer) >= self.max_length and flag not in ['v']:
                result.append(buffer)
                buffer = ""

        if buffer:
            result.append(buffer)

        return result

    def segment_japanese(self, text):
        tokens = list(self.janome.tokenize(text))
        buffer, result = "", []

        for token in tokens:
            buffer += token.surface
            if len(buffer) >= self.max_length:
                result.append(buffer)
                buffer = ""

        if buffer:
            result.append(buffer)

        return result

    def bert_adjustment(self, segments):
        new_segments = []
        for seg in segments:
            if len(seg) <= self.max_length:
                new_segments.append(seg)
                continue
            
            best_split = None
            best_score = -float("inf")

            for i in range(self.max_length - 5, self.max_length + 6):
                if i >= len(seg):
                    break
                masked_seg = seg[:i] + "[MASK]" + seg[i+1:]
                inputs = self.bert_tokenizer(masked_seg, return_tensors="pt")
                with torch.no_grad():
                    outputs = self.bert_model(**inputs)
                mask_token_logits = outputs.logits[0, i]
                mask_token_prob = mask_token_logits.softmax(dim=0)
                token_prob = mask_token_prob[self.bert_tokenizer.convert_tokens_to_ids(seg[i])]
                if token_prob > best_score:
                    best_score = token_prob.item()
                    best_split = i

            if best_split:
                new_segments.extend([seg[:best_split], seg[best_split:]])
            else:
                new_segments.append(seg)

        return new_segments

    def segment(self, text):
        lang = self.detect_language(text)

        if lang == 'zh':
            segments = self.segment_chinese(text)
        elif lang == 'ja':
            segments = self.segment_japanese(text)
        else:
            raise ValueError("Unable to determine text language.")

        return self.bert_adjustment(segments)

# ファイルからテキストを読み込む
with open(file_path, "r", encoding="utf-8") as file:
    text_content = file.read()

segmenter = TextSegmentation(max_length=25, model_name="bert-base-multilingual-cased")
print(segmenter.segment(text_content))

```

**コードの説明**:
1. 必要なライブラリやモジュールをインポートします。
2. `TextSegmentation`というクラスを定義します。このクラスは、テキストのセグメンテーションを行う主要なロジックを持っています。
3. 初期化関数`__init__`で、BERTを含む各種モデルをロードします。
4. `detect_language`関数は、テキストの言語を検出します。
5. `segment_chinese`と`segment_japanese`関数は、それぞれ中国語と日本語のテキストをセグメントに分割します。
6. `bert_adjustment`関数は、BERTモデルを使ってセグメンテーションの精度を向上させます。
7. `segment`関数は、テキストをセグメンテーションに分割する主要な関数です。

**Linuxにインストールする方法**:
**テキスト分割プログラムの環境構築手順**

1. **Pythonのインストール**
    - Pythonの公式サイトから最新のPythonをダウンロードしてインストールします。  
      [公式ダウンロードページ](https://www.python.org/downloads/)
    - インストールが完了したら、ターミナルやコマンドプロンプトで`python --version`と入力して、インストールが正しく完了したかを確認します。

2. **必要なライブラリのインストール**
    - 下記のコマンドをターミナルやコマンドプロンプトで実行して、必要なライブラリをインストールします。
      ```
      pip install janome jieba transformers torch
      ```

3. **プログラムの準備**
    - 与えられたプログラムをテキストエディタで開きます。
    - `file_path = "/home/user/Dropbox/text.txt"`の部分を、分割したいテキストファイルのパスに適宜変更します。

4. **プログラムの実行**
    - ターミナルやコマンドプロンプトを開き、プログラムが保存されているディレクトリに移動します。
    - 以下のコマンドを実行してプログラムを起動します。
      ```
      python text_segmentation.py
      ```

5. **BERTモデルのダウンロード**
    - プログラムを初めて実行する際、`transformers`ライブラリが自動的にBERTモデルをダウンロードします。このモデルはキャッシュに保存され、次回以降の実行では再ダウンロードされません。

6. **結果の確認**
    - プログラムがテキストを適切に分割して出力します。出力結果を確認して、期待した結果が得られたかを確認します。

以上がテキスト分割プログラムの環境構築手順となります。



追記:自然な改行を重視しつつ、テキストを25文字前後のセグメントに分けるためのプログラムに修正しました。
```
import sys
import jieba
import jieba.posseg as pseg
from janome.tokenizer import Tokenizer as JanomeTokenizer
from transformers import BertTokenizer, BertForMaskedLM
import torch
import re

class TextSegmentation:
    def __init__(self, max_length=25, model_name="bert-base-multilingual-cased"):
        self.max_length = max_length

        # Initialize models
        self.janome = JanomeTokenizer()
        jieba.initialize()
        self.bert_tokenizer = BertTokenizer.from_pretrained(model_name)
        self.bert_model = BertForMaskedLM.from_pretrained(model_name).eval()

    def detect_language(self, text):
        if any('\u4e00' <= ch <= '\u9fff' for ch in text):
            return 'zh'
        elif any('\u3040' <= ch <= '\u30ff' for ch in text):
            return 'ja'
        elif re.search(r'[a-zA-Z]', text):
            return 'en'
        return 'unknown'

    def segment_chinese(self, text):
        words = pseg.cut(text)
        sentences = []
        current_sentence = ''
        current_length = 0

        for word, flag in words:
            if current_length + len(word) > self.max_length or word in ['。', '？', '！']:
                sentences.append(current_sentence)
                current_sentence = ''
                current_length = 0
            current_sentence += word
            current_length += len(word)

        if current_sentence:
            sentences.append(current_sentence)

        return sentences

    def segment_japanese(self, text):
        tokens = list(self.janome.tokenize(text))
        buffer, result = "", []

        for token in tokens:
            buffer += token.surface

            if token.surface in ['。', '？', '！'] or len(buffer) >= self.max_length:
                result.append(buffer)
                buffer = ""

        if buffer:
            result.append(buffer)

        return result

    def segment_english(self, text):
        sentences = re.split(r'(?<=[.!?])\s+', text)
        result = []

        buffer = ""
        for sentence in sentences:
            if len(buffer) + len(sentence) <= self.max_length or sentence in ['.', '!', '?']:
                buffer += " " + sentence.strip()
            else:
                result.append(buffer.strip())
                buffer = sentence.strip()

        if buffer:
            result.append(buffer)

        return result

    def segment(self, text):
        lang = self.detect_language(text)

        if lang == 'zh':
            return self.segment_chinese(text)
        elif lang == 'ja':
            return self.segment_japanese(text)
        elif lang == 'en':
            return self.segment_english(text)
        else:
            raise ValueError("Unable to determine text language.")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python3 txt_splitter.py <input_file_path> <output_file_path>")
        sys.exit(1)

    input_file_path = sys.argv[1]
    output_file_path = sys.argv[2]

    with open(input_file_path, "r", encoding="utf-8") as file:
        text_content = file.read()

    segmenter = TextSegmentation(max_length=25, model_name="bert-base-multilingual-cased")
    segments = segmenter.segment(text_content)

    with open(output_file_path, "w", encoding="utf-8") as file:
        for seg in segments:
            file.write(seg + "\n")

```

```
python3 txt_splitter.py splitter-input.txt splitter-output.txt
```
コードに英語のテキスト分割ロジックを追加し、言語判別の部分も更新しました。これにより、英語、日本語、中国語のテキストを適切に処理することができます。