そして、はい-コード内の空白行もカウントされます。記事の最後に、作業の簡単なデモンストレーションがあります。
私たちは、必要のpython3でダウンロードし、たTesseract 5、及びdistiluse塩基多言語-ケース入りモデルから文-トランスフォーマーパッケージを。次に何が起こるかをすでに理解している人は面白くないでしょう。
それまでの間、必要なものはすべて次のようになります。
最初の18行
import numpy as np
import os, sys, glob
os.environ['PATH'] += os.pathsep + os.path.join(os.getcwd(), 'Tesseract-OCR')
extensions = [
'.xlsx', '.docx', '.pptx',
'.pdf', '.txt', '.md', '.htm', 'html',
'.jpg', '.jpeg', '.png', '.gif'
]
import warnings; warnings.filterwarnings('ignore')
import torch, textract, pdfplumber
from cleantext import clean
from razdel import sentenize
from sklearn.neighbors import NearestNeighbors
from sentence_transformers import SentenceTransformer
embedder = SentenceTransformer('./distillUSE')
ご覧のとおり、それはきちんと必要になり、すべての準備が整っているように見えますが、ファイルなしでは実行できません。特に、textract(有料のAmazonからではない)は、pdfplumberを使用できるため、ロシアのpdfではうまく機能しません。さらに、テキストを文に分割することは困難な作業であり、この場合、razdelはロシア語で優れた仕事をします。
聞いたことがない人はscikit-学ぶ-
主なことは、実際に(任意の)ファイルのテキストをベクトルに変換することです。これは、彼らが行うことです。
次の36行のコード
def processor(path, embedder):
try:
if path.lower().endswith('.pdf'):
with pdfplumber.open(path) as pdf:
if len(pdf.pages):
text = ' '.join([
page.extract_text() or '' for page in pdf.pages if page
])
elif path.lower().endswith('.md') or path.lower().endswith('.txt'):
with open(path, 'r', encoding='UTF-8') as fd:
text = fd.read()
else:
text = textract.process(path, language='rus+eng').decode('UTF-8')
if path.lower()[-4:] in ['.jpg', 'jpeg', '.gif', '.png']:
text = clean(
text,
fix_unicode=False, lang='ru', to_ascii=False, lower=False,
no_line_breaks=True
)
else:
text = clean(
text,
lang='ru', to_ascii=False, lower=False, no_line_breaks=True
)
sentences = list(map(lambda substring: substring.text, sentenize(text)))
except Exception as exception:
return None
if not len(sentences):
return None
return {
'filepath': [path] * len(sentences),
'sentences': sentences,
'vectors': [vector.astype(float).tolist() for vector in embedder.encode(
sentences
)]
}
それでは、テクニックの問題が残っています。すべてのファイルを調べ、ベクトルを抽出し、余弦距離によってクエリに最も近いものを見つけることです。
残りのコード
def indexer(files, embedder):
for file in files:
processed = processor(file, embedder)
if processed is not None:
yield processed
def counter(path):
if not os.path.exists(path):
return None
for file in glob.iglob(path + '/**', recursive=True):
extension = os.path.splitext(file)[1].lower()
if extension in extensions:
yield file
def search(engine, text, sentences, files):
indices = engine.kneighbors(
embedder.encode([text])[0].astype(float).reshape(1, -1),
return_distance=True
)
distance = indices[0][0][0]
position = indices[1][0][0]
print(
' "%.3f' % (1 - distance / 2),
': "%s", "%s"' % (sentences[position], files[position])
)
print(' "%s"' % sys.argv[1])
paths = list(counter(sys.argv[1]))
print(' "%s"' % sys.argv[1])
db = list(indexer(paths, embedder))
sentences, files, vectors = [], [], []
for item in db:
sentences += item['sentences']
files += item['filepath']
vectors += item['vectors']
engine = NearestNeighbors(n_neighbors=1, metric='cosine').fit(
np.array(vectors).reshape(len(vectors), -1)
)
query = input(' : ')
while query:
search(engine, query, sentences, files)
query = input(' : ')
次のようにすべてのコードを実行できます。
python3 app.py /path/to/your/files/
それがコードのやり方です。
そして、これが約束されたデモです。
「Lenta.ru」から2つのニュースを受け取り、1つを悪名高いペイントを介してgifファイルに入れ、もう1つをテキストファイルに入れました。
First.gifファイル
2番目の.txtファイル
, . .
, - . , , , . . , .
, , , . . .
, - - .
, №71 , , , . 10 , . — .
, - . , , , . . , .
, , , . . .
, - - .
, №71 , , , . 10 , . — .
そして、これがどのように機能するかのgifアニメーションです。もちろん、GPUを使用すると、すべてがより明るく機能します。
読んでくれてありがとう!私はまだこの方法が誰かに役立つことを願っています。