Pythonランキング検索エンジンの実装(パート1)

ニュースフィードを見ると、Aakash Japiによって書かれた記事「Pythonでのランキングを使用した検索エンジンの実装」について、典型的なプログラマーからの推奨事項に出会いました。 彼女は私に興味を持っていて、Runetには同じような資料はあまりないので、翻訳することにしました。 かなり大きいので、2〜3つの部分に分けます。 ここで紹介を終え、翻訳に移ります。



Quoraを使用するたびに、少なくとも次のような質問が表示されます。Googleがどのように機能し、情報を見つける際にどのようにそれを打ち負かすことができるのか、誰も尋ねませんか。 ほとんどの質問はこれほど大胆で誤解を招くものではありませんが、それらはすべて同様の感情を表しており、この点で検索エンジンがどのように機能するかについての重大な誤解を伝えています。



しかし、Googleは非常に複雑ですが、一致を検索して検索クエリの結果を評価(ランク)する検索エンジンの基本概念は特に難しくはなく、基本的なプログラミングの経験がある人なら誰でも理解できます。 現時点では検索でGoogleを上回ることはできないと思いますが、検索エンジンを作成することは達成可能な目標であり、実際、かなり有益な演習であるため、試してみることをお勧めします。



これは、この記事で説明するものです。標準クエリ(クエリ内の少なくとも1つの単語がドキュメント内にある)とフレーズ全体(フレーズ全体がテキストに表示される)を処理できるローカルテキストファイル用の検索エンジンの作成方法ベースTF-IDFスキームを使用してランク付けできます。



検索エンジンの開発には、主に2つの手順があります。インデックスを作成し、次にインデックスを使用してクエリに答えます。 そして、評価の結果(TF-IDF、PageRankなど)、リクエスト/ドキュメントの分類、および場合によってはユーザーの最後のクエリを追跡するための小さな機械学習を追加し、これに基づいて検索エンジンのパフォーマンスを向上させる結果を選択できます。



それでは、苦労せずに始めましょう!



インデックス構築



したがって、テキスト検索エンジンを構築する最初のステップは、逆索引を構築することです。 それが何であるか説明させてください。 反転インデックスは、マーカーを表示するドキュメントにマーカーをマップするデータ構造です。 このコンテキストでは、マーカーを単純に単語と見なすことができます。したがって、逆索引は基本的に単語を受け取り、出現するドキュメントのリストを返します。



ただし、最初に、一連のドキュメントを分析してマーク(マーク、単語に分割)する必要があります。 これを次のように行います。インデックスに追加するドキュメントごとに、すべての句読点を削除してスペースに分割し、ドキュメントの名前をマーカーのリストにマップする一時ハッシュテーブルを作成します。 上記で説明した最終的な逆インデックスに到達するまで、このハッシュテーブルを数回変換します(ただし、少し複雑ですが、これについては後で説明します)。 最初のテキストフィルタリングを行うコードは次のとおりです。



def process_files(self): file_to_terms = {} for file in self.filenames: pattern = re.compile('[\W_]+') file_to_terms[file] = open(file, 'r').read().lower(); file_to_terms[file] = pattern.sub(' ',file_to_terms[file]) re.sub(r'[\W_]+','', file_to_terms[file]) file_to_terms[file] = file_to_terms[file].split() return file_to_terms
      
      





ここで行っていないことは2つありますが、行うことをお勧めします。 すべてのストップワード(「how」、「and」、「so」など、ドキュメントに関連性​​を加えない)を削除し、すべてのワードを変換します(この方法で「run」と「runner」が「run」に変わります)。外部ライブラリを使用します(ただし、これによりインデックス作成が遅くなります)。



これで、私が言及した逆索引がドキュメントの名前までのワードマップになることはわかっていますが、フレーズだけでなく、特定のシーケンス内の単語に対するクエリもサポートする必要があります。 これを行うには、各単語がドキュメント内のどこにあるかを知る必要があるため、単語の順序を確認できます。 ドキュメント上の箇条書きリストの各単語に、そのドキュメント内の単語の位置としてインデックスを付けるため、最終的な逆インデックスは次のようになります。

 {word: {documentID: [pos1, pos2, ...]}, ...}, ...}
      
      





これの代わりに:

 {word: [documentID, ...], ...}
      
      





したがって、最初のタスクは、各ドキュメントの位置のワードマッピングを作成し、それらを結合して完全な逆インデックスを作成することです。 次のようになります。



 #input = [word1, word2, ...] #output = {word1: [pos1, pos2], word2: [pos2, pos434], ...} def index_one_file(termlist): fileIndex = {} for index, word in enumerate(termlist): if word in fileIndex.keys(): fileIndex[word].append(index) else: fileIndex[word] = [index] return fileIndex
      
      





このコードは非常に簡単です:文書内の用語のリストをスペースで区切って(単語は元の順序で)受け取り、それぞれをハッシュテーブルに追加します。値は文書内のこの単語の位置のリストです。 このリストを何度も作成し、すべての単語を調べて、行のキーを備えたテーブルを残し、これらの行の位置のリストまでマークアップします。



次に、これらのハッシュテーブルを組み合わせる必要があります。 中間インデックス形式を作成することから始めました

 {documentID: {word: [pos1, pos2, ...]}, ...}
      
      





その後、最終インデックスに変換します。 これはここで行われます:



 #input = {filename: [word1, word2, ...], ...} #res = {filename: {word: [pos1, pos2, ...]}, ...} def make_indices(termlists): total = {} for filename in termlists.keys(): total[filename] = index_one_file(termlists[filename]) return total
      
      





このコードは非常にシンプルです。file_to_terms関数の結果を単純に受け入れ、ファイル名のキーと前の関数の結果である値でマークされた新しいハッシュテーブルを作成し、ネストされたハッシュテーブルを作成します。



次に、実際に逆索引を作成できます。 コードは次のとおりです。



 #input = {filename: {word: [pos1, pos2, ...], ... }} #res = {word: {filename: [pos1, pos2]}, ...}, ...} def fullIndex(regdex): total_index = {} for filename in regdex.keys(): for word in regdex[filename].keys(): if word in total_index.keys(): if filename in total_index[word].keys(): total_index[word][filename].extend(regdex[filename][word][:]) else: total_index[word][filename] = regdex[filename][word] else: total_index[word] = {filename: regdex[filename][word]} return total_index
      
      







それでは、これを分解してみましょう。 最初に、単純なハッシュテーブル(Python辞書)を作成し、2つのネストされたループを使用して、入力ハッシュの各単語を反復処理します。 次に、この単語が出力ハッシュテーブルにキーとして存在するかどうかを最初にチェックします。 そうでない場合は、ドキュメント(この場合は変数filenameで識別される)をこの単語の位置のリストに一致させる値として別のハッシュテーブルを設定して追加します



これがキーである場合、別のチェックを実行します。現在のドキュメントが単語の各ハッシュテーブルにあるかどうか(ファイル名と単語の位置が一致するもの)。 これがキーである場合、別のチェックを行います。現在のドキュメントが各単語のハッシュテーブルにあるかどうか(ファイル名と単語の位置を一致させるもの)。 その場合、現在の位置のリストをこの位置のリストで拡張します(このケースは完全を期すために残されていることに注意してください:各単語には各ファイル(ファイル名)の位置のリストが1つしかないため、これは起こりません)。 そうでない場合は、このファイル(ファイル名)の位置リストに等しい位置を設定します。



そして今、インデックスがあります。 単語を入力することができ、それが表示されるドキュメントのリストを取得する必要があります。 次の記事では、このインデックスでクエリを実行する方法を示します。



(実装と一緒に)すべての部分で使用されるすべてのコードは、 GitHubで利用できます



PSこの部分は終了します。 私がすべてを非常に理解できるように、そして最も重要なことには-正しく翻訳したことを願っています。 デザインと翻訳に関するコメントとアドバイスを受け入れる準備ができています。



All Articles