オープンソースで文章要約と翻訳、音声合成をさせてみた【PEGASUS/GoogleTextToSpeech/GoogleTrans】

# 概要

テキストファイルを要約して、音声合成してくれるプログラムです。

要点だけをまとめてくれるので、用途はビジネスマンや学生が長い文章を読む時間がない時に、さらっと把握する時に使えると思います。。


言語対応としては、英語と日本語の両方に対応しており、

インプット：英語 or 日本語
アウトプット：英語 or 日本語

を可変させることができます。
特に、インプットに関しては英語か日本語かを自動で判定してくれるので、プログラム実行時に明示する必要がありません。

プログラムの処理の流れは下の図のような流れとなっています。

![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687015/70c23315-6dc6-c63c-a770-e294f2b88409.png)



## 環境
GoogleColabの環境で動かせます。ローカルのJupyterNotebookやpyファイルでも動かせますが、GPUを使えないと厳しいです。


## 利用するライブラリ

主に次の4つを使っています。
- 言語判定のためのライブラリ：
- 要約のためのライブラリ
- 翻訳のためのライブラリ
- 音声合成のライブラリ


### ライブラリの入れ方

``` python
# 言語判定のライブラリ
!pip install langid

# 要約関連のライブラリを入れる
# ここのページでPyTorchのインストールコマンドをセットする（具体的なやり方は後述）
# ページ：https://pytorch.org/get-started/locally/
!pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu116
!pip install transformers
!pip install sentencepiece


# 翻訳のライブラリ
!pip install googletrans==4.0.0-rc1


# 音声合成：Googleの text to speechのインストール
!pip install gTTS
```

### PyTorchの入れ方の説明

PyTorchはOSやGPU環境によって、ライブラリを入れるためのコマンドが異なっているので、公式ページから入れ方を調べる必要があります。

公式ページで調べるとなると面倒くさそうに聞こえますが、簡単です。公式ページに行くと、自分が動かしたい環境をポチポチ選択すれば自動で最適なコマンドを出力してくれるので、それをコピペしてGoogleColab(またはローカルのコマンドプロンプト/ターミナル)に入力するだけでOKです。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687015/00e7f734-e649-2811-9bf3-d096653caf8a.png)

OSやPackage、Language、ComputePlatformを選び、GoogleColab環境の人であれば、赤枠で囲っているところをコピペします。

## 事前準備

### ランタイムの変更
今回はGPUで実行するので、GoogleColabを使う場合は次の手順でGPU実行の設定を行います。

画面上のほうに、「ランタイム」というのがあるのでクリックします。
![図4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687015/ece3ad91-d60d-16a1-051b-19250399197c.png)


すると、下のような画面が出てくるので、「ランタイムの変更」をクリックします。
![図5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687015/e3bf7554-11bd-9b0b-8e01-40626b678ca0.png)


画像のように「GPU」を選択し、保存します。

![図6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687015/bf0d1692-89eb-a12c-b5a3-0635915ead9f.png)

これでGoogleColab上でGPUを使うための準備が整いました。

### ディレクトリの作成
GoogleColab上でディレクトリ(フォルダ)「SttS」を作成し、その中に「input」「output」というディレクトリ(フォルダ)を作ります。

ので、ディレクトリ構造は下のようになります。

```
└ /content/drive/MyDrive/colab/SttS/
　　├ SttS.ipynb
　　├ input/
　　└ output/
```

「input」フォルダの中に要約/音声合成させたいテキストファイルを「src_text.txt」というファイル名で置いておきます。


最終的には、次のようなディレクトリ構造になります。

```
└ /content/drive/MyDrive/colab/SttS/
　　├ SttS.ipynb
　　├ input/
        └ src_text.txt
　　└ output/
```


## コードの説明

### ライブラリをインポート

``` python
# 各種ライブラリのインポート

# 言語判定のライブラリ
import langid


# 要約関連のライブラリ
from transformers import PegasusForConditionalGeneration, PegasusTokenizer 
import torch 


# 翻訳関連のライブラリ
from googletrans import Translator


# 音声発話のためのライブラリ
from gtts import gTTS


# Jupyterファイル上でwavファイルを再生できるようにするためのライブラリ
from IPython.display import Audio


import pandas as pd
import numpy as np
```

### GoogleColabのお作法を実行

今回は要約、音声合成させたいテキストをテキストファイル(.txt)のGoogleColab環境にアップロードします。

そのため、GoogleColabで下のようなコードを実行させる必要があります。

``` python
from google.colab import drive
drive.mount('/content/drive')
```

実行すると色々出てきますが、自分のGoogleアカウントを選択し、「許可する」を選択すればOKです。

### ファイル名などの設定

筆者は事前にプログラムの実行ルールやファイルの場所などを指定しておくことが好きなので、ここでそれらの設定をします。

``` python
# ファイル名の設定
SRC_FILE_NAME    = "src_text.txt" # 入力テキストファイルの名前
OUTPUT_WAV_NAME  = "output.wav"   # 出力wavファイルの名前
OUTPUT_TEXT_NAME = "output.txt"   # 出力テキストファイルの名前


# 動作周りの設定
IS_SUMMARISE = True # 入力した文章を要約したいときは、"True"。要約しないのであれば、"False"
IS_JAPANISE  = True # 音声合成を英語にするのか、日本語にするのか設定。日本語にしたいときは、"True"、英語の場合は、"False"
```

上の方で『事前準備』のところで設定したディレクトリ構造をセットしておきます。
``` python
# 各種ファイルのディレクトリ設定

DIR_INPUT  = "/content/drive/MyDrive/colab/SttS/input/"
DIR_OUTPUT = "/content/drive/MyDrive/colab/SttS/output/"
```

### テキストファイルの読み込み

要約/翻訳させたいテキストファイルを読み込ませます。
英語でも日本語でもOKです。

``` python
with open(DIR_INPUT + SRC_FILE_NAME) as f:
    src_text = f.read()
```

### テキストファイルの言語判定をする関数を作成

``` python
# 言語判定の関数
# 日本語であれば"ja"、英語であれば、"en"
def ClassifyLanguage(text = "default text"):
  """
  input:text (str)
  output:what language is source text data (str)
  """
  result = langid.classify(text)

  return result[0]
```

### 日本語→英語への翻訳を実行する関数を作成

今回使用している、PEGASUSという要約モデルは英語しか対応していないので、テキストファイルが日本語の場合は英語に翻訳する必要があります。その処理をここの関数で実施させます。

``` python
# 日本語→英語への翻訳
def MakeTextEnglish(text = "デフォルトの文章", src_lang = "ja"):
  """
  input: Japanese text (str)
  output: English text (str)
  """
  tr = Translator()

  while True:
    try:
      result = tr.translate(text, dest="en").text
      break
    except Exception as e:
      tr = Translator()

  return result
```

### 要約を実行する関数を作成

PEGASUSというモデルを使った「要約」を実行させる関数を定義します。


``` python
# 要約を実行する
def SummarisationText(text = "default text"):
  """
  input: English text (str)
  output: summarised English text (str)
  """
  # モデル読み込み 
  model_name = 'google/pegasus-large'
  tokenizer  = PegasusTokenizer.from_pretrained(model_name)

  # デバイスの設定
  device = 'cuda' if torch.cuda.is_available() else 'cpu'

  # 要約の実行
  model     = PegasusForConditionalGeneration.from_pretrained(model_name).to(device) 
  batch     = tokenizer(text, 
                        truncation=True,
                        padding='longest',
                        return_tensors="pt",
                        max_length = 1024).to(device) 
  translated = model.generate(**batch)
  list_summarised_text   = tokenizer.batch_decode(translated, skip_special_tokens=True)

  result = list_summarised_text[0] # 今回は1つだけの文章だからインデックスはゼロ

  return result
```

### 英語→日本語への翻訳を実行する関数を作成

要約自体が英語で実行されるので、このままではアウトプットが英語になってしまいます。そこで、最終アウトプットを日本語にしたい場合は、ここの関数で英語→日本語日本語に直します。

``` python
# 英語→日本語への翻訳
def MakeTextJapanese(text = "default text", src_lang = "en"):
  """
  input: English text (str)
  output: Japanese text (str)
  """
  text = text.replace("\n", " ")

  tr = Translator()

  while True:
    try:
      result = tr.translate(text, dest="ja").text
      break
    except Exception as e:
      tr = Translator()

  return result
```

### 音声合成を実行する関数を作成

音声合成を実行する関数をここで定義します。

``` python
# テキストデータから音声ファイル(wavファイル)を作成する
def GenerateWavFile(text_to_say = "こんにちは", language = "ja"):
  gtts_object = gTTS(text = text_to_say, 
                     lang = language,
                     slow = False)

  gtts_object.save(DIR_OUTPUT + OUTPUT_WAV_NAME)
```

### バグった要約文を修正するための関数を定義

要約結果がたまにバグることがあります。バグ内容は、「同じ単語が連続し続ける」というものです。なぜかはわからないのですがたまに要約がバグり、同じ単語がずぅうううっと続いてしまうことがあるので、連続した単語を1単語に直してあげる必要があります。そのための処理をここで定義します。


``` python

# 要約でたまにバグる（同じ単語が永遠と続いてしまう）ので、
# それが起きても、問題がないように、
# 連続して同じ単語が続いた場合は一単語に直す
def DeleteDuplicatedText(text = "a a a a"):
  #text = text.replace("\n", " ")
  data = text.split(' ')

  df = pd.Series(data)

  # 一つ手前の要素と異なる要素だけを抜き出す
  df = df[df != df.shift(1)]

  list_ = df.values.tolist()

  result = ' '.join(list_)

  return result
```

### 定義してきた関数を実行する。

まずは言語判定。
``` python
# src_text.txtの言語を判定
str_language = ClassifyLanguage(text = src_text)
```

日本語であれば、英語に翻訳させます。英語であれば、翻訳させずにそのまま次の処理に進ませます。
``` python
is_English = str_language == 'en'

# そもそもの言語が英語であれば、翻訳する必要ないので、
# 要約対象のデータとして"target_smmrs_text"に格納する
if is_English:
  target_smmrs_text = src_text
else: #英語以外の言語であれば、英語に翻訳する
  translated_text   = MakeTextEnglish(text = src_text, src_lang = str_language)
  target_smmrs_text = translated_text
```

要約の実行
``` python
# 要約の実行

if IS_SUMMARISE:
  target_trsnslt_text = SummarisationText(text = target_smmrs_text)
else:
  target_trsnslt_text = target_smmrs_text

# 要約文でバグった時の処理をここで実行します
target_trsnslt_text = DeleteDuplicatedText(target_trsnslt_text)
```

音声合成。
```python
# 音声合成の言語を設定して、その言語のテキストファイルを生成する

if IS_JAPANISE:
  target_spch_text = MakeTextJapanese(text = target_trsnslt_text)
  GenerateWavFile(text_to_say = target_spch_text, language = "ja")
else:
  target_spch_text = target_trsnslt_text
  GenerateWavFile(text_to_say = target_spch_text, language = "en")
```

GoogleColab上で音声合成されたファイルを聞きたい場合は、次のコードを実行します。
```python
Audio(DIR_OUTPUT + OUTPUT_WAV_NAME)
```
実行すると下のような音声再生UIが表示されるので、聞きたい場合は再生ボタンをクリックします。

![図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/687015/76d8ab69-49d3-a802-e4a8-6945873def9c.png)


音声合成ではなく、テキストで確認したい場合は次のコードを実行します。
```python
with open(DIR_OUTPUT + OUTPUT_TEXT_NAME, mode='w') as f:
  f.write(target_spch_text)
```

## まとめと感想
今回は要約のために「**PEGASUS**」というモデルを使ってみました。たまにバグることに目をつぶれば、要約の精度はかなり高いと思います。最近流行しているChatGPTなどとの要約精度の比較はしていないので、要約精度について言えることは少ないのですが、実用上"使える"レベルだと個人的に思っています。

今後ですが、翻訳の精度がGoogleTransだと低いので、ここの改善が必要かなと思います。実は、Googletransではなく、DeepLのAPIを使って翻訳させるともっと自然な日本語になるので、実用性をさらに追及したい方はそちらを使ってみたり、[こちらに投稿したようなLangChainというライブラリ](https://qiita.com/KENTAROSZK/items/fda5bb3d1fba4b454450)を使って翻訳させてみるのも良いかもしれません。