Closed8

Haystackチュートリアルをやってみる: Preprocessing Your Documents

kun432kun432

https://haystack.deepset.ai/tutorials/08_preprocessing

HaystackのDocumentStoreは、辞書型のデータの配列であることを期待している。以下のようなフォーマットになる。

docs = [
    {
        'content': DOCUMENT_TEXT_HERE,
        'meta': {'name': DOCUMENT_NAME, ...}
    }, ...
]

ということで、いろいろなファイルタイプのドキュメントを上記のフォーマットにしていくために用意されている前処理ツールの話。

kun432kun432

Colaboratoryで進める。今回はGPUは不要。

インストール。ファイルフォーマット関連や前処理に必要なオプションが有効化されているのがわかる。

%%bash

pip install --upgrade pip
pip install farm-haystack[colab,ocr,preprocessing,file-conversion,pdf]

テレメトリー有効化。

from haystack.telemetry import tutorial_running

tutorial_running(8)

ロギング設定。

import logging

logging.basicConfig(format="%(levelname)s - %(name)s -  %(message)s", level=logging.WARNING)
logging.getLogger("haystack").setLevel(logging.INFO)
kun432kun432

サンプルデータを取得。

from haystack.utils import fetch_archive_from_http

doc_dir = "data/tutorial8"
s3_url = "https://s3.eu-central-1.amazonaws.com/deepset.ai-farm-qa/datasets/documents/preprocessing_tutorial8.zip"
fetch_archive_from_http(url=s3_url, output_dir=doc_dir)

サンプルのデータは以下の3つ。

!find data -type f -ls

Word文書、構造化されていないテキストデータ、あとPDF。

  1573070     16 -rw-r--r--   1 root     root        12433 Oct  3 10:31 data/tutorial8/heavy_metal.docx
  1573057     28 -rw-r--r--   1 root     root        27802 Oct  3 10:31 data/tutorial8/classics.txt
  1573056    760 -rw-r--r--   1 root     root       775166 Oct  3 10:31 data/tutorial8/bert.pdf
kun432kun432

Converters

Convertersクラスを使うと、一般的なファイルタイプをHaystackのパイプラインで使えるドキュメントに変換してくれる。

以下の例では、TextConverter/PDFToTextConverter/DocxToTextConverterを使って変換している。

from haystack.nodes import TextConverter, PDFToTextConverter, DocxToTextConverter, PreProcessor


converter = TextConverter(remove_numeric_tables=True, valid_languages=["en"])
doc_txt = converter.convert(file_path="data/tutorial8/classics.txt", meta=None)[0]

converter = PDFToTextConverter(remove_numeric_tables=True, valid_languages=["en"])
doc_pdf = converter.convert(file_path="data/tutorial8/bert.pdf", meta=None)[0]

converter = DocxToTextConverter(remove_numeric_tables=False, valid_languages=["en"])
doc_docx = converter.convert(file_path="data/tutorial8/heavy_metal.docx", meta=None)[0]

valid_languagesは言語変換ではなく、正しい言語で変換されているかのチェックのためのものらしい。

あと、Apache Tikaを利用したConverterもあるらしい。

https://github.com/deepset-ai/haystack/blob/main/haystack/nodes/file_converter/tika.py

こういうやつか

https://vintage.ne.jp/blog/2014/10/346

あとまるっとよしなにやってくれるユーティリティもある

from haystack.utils import convert_files_to_docs

all_docs = convert_files_to_docs(dir_path=doc_dir)
kun432kun432

PreProcessor / Cleaning / Splitting

PreProcessorクラスはテキストをクリーニングしたり分割したりするのに使う。

from haystack.nodes import PreProcessor

preprocessor = PreProcessor(
    clean_empty_lines=True,
    clean_whitespace=True,
    clean_header_footer=False,
    split_by="word",
    split_length=100,
    split_respect_sentence_boundary=True,
)
docs_default = preprocessor.process([doc_txt])
print(f"n_docs_input: 1\nn_docs_output: {len(docs_default)}")

上記の例だと51個のドキュメントに分割されている。

n_docs_output: 51

クリーニング関連のオプション

  • clean_empty_lines: 3行以上の空行を2行の空行に変換
  • clean_whitespace: 行頭・行末の空白を除去
  • clean_header_footer: ページごとに繰り返されるヘッダー・フッター行を削除

分割のオプション

  • split_by
    • : 分割単位。以下より選択
      • "word": 単語単位。セパレータが"\n"と同じことだと思う。
      • "sentence": 文単位。セパレータが"."と同じことだと思う。
      • "paragraph": 段落単位。セパレータが"\n\n"と同じことだと思う。
      • ※細かく追いかけていないので上記は推測。
  • split_length
    • 分割する数。
    • split_by:"word"だったらn語みたいな感じだと思う。
  • split_overlap: 分割時に重複させる数。
  • split_respect_sentence_boundary
    • 文の境界を跨ぐか?
    • デフォルトだと文の途中で分割しない
    • Falseに設定すると文の途中で分割する
kun432kun432

全部まとめるとこんな感じ

from haystack.utils import fetch_archive_from_http
from haystack.utils import convert_files_to_docs
from haystack.nodes import PreProcessor

doc_dir = "data/tutorial8"
s3_url = "https://s3.eu-central-1.amazonaws.com/deepset.ai-farm-qa/datasets/documents/preprocessing_tutorial8.zip"
fetch_archive_from_http(url=s3_url, output_dir=doc_dir)

all_docs = convert_files_to_docs(dir_path=doc_dir)

preprocessor = PreProcessor(
    clean_empty_lines=True,
    clean_whitespace=True,
    clean_header_footer=False,
    split_by="word",
    split_length=100,
    split_respect_sentence_boundary=True,
)
docs = preprocessor.process(all_docs)

print(f"n_files_input: {len(all_docs)}\nn_docs_output: {len(docs)}")
n_docs_output: 174
このスクラップは2023/10/03にクローズされました