top of page

特許文章の要約のembeddingと検索

更新日:3月29日

1.はじめに

  • google vertex aiのtextembedding-gecko-multilingualを使って、各国特許の要約のベクトル表現を作成して検索しました。


2.ベクトルデータベース作りと検索

  • 検索をする際にキーワードではなく、文章からベクトルデータを使って、そのベクトルデータを使って検索したいと思います。

  • 商用DBにはAPIが色々ありそうですが、とりあえず気軽に呼び出すべく、自作してみました。

(1)要約データを取得する

  • これは毎度使わせてもらっているgoogle patents public databaseで取得します。

  • 全データだと1億レコードくらいになってしまうので、2020年以降かつCN特許を除いて取得します。

  • ※CNデータを含むと大変な数(2400万件くらい)になってしまったので、省略しました。省略後でだいたい220万件ほど。


SELECT
  family_id,
  ARRAY_AGG(DISTINCT application_number) as appnum,
  ARRAY_AGG(DISTINCT filing_date) as filing_date,
  ARRAY_AGG(DISTINCT publication_number) as pubnum,
  SPLIT(STRING_AGG(DISTINCT abst.text,"|"),"|")[0] as abst
FROM
  `patents-public-data.patents.publications_202307`,
  UNNEST(abstract_localized) AS abst
  WHERE CAST(filing_date AS INT64) > 20200000
  AND NOT SUBSTR(application_number, 0,2) = "CN"
  GROUP BY family_id



(2)embedding

  • OpenAIのada(1536次元)もありましたが、やや割安かつVertex AIの中に含まれるtextembedding-gecko-multilingualでベクトル表現(768次元)に変換します。

!pip3 install google-cloud-aiplatform
!pip3 install shapely==2.0.0
exit
PROJECT_ID = "***"  # @param {type:"string"}
LOCATION = "us-central1"  # @param {type:"string"}

import vertexai
vertexai.init(project=PROJECT_ID, location=LOCATION)
from vertexai.preview.language_models import TextEmbeddingModel
model = TextEmbeddingModel.from_pretrained("textembedding-gecko-multilingual@latest")

  • こちらのソースコードを借りつつ、20000件づつ処理してjsonファイルとして出力していきます。

#@title jsonで作成してGCSに入れる。
#全レコード数
BQ_NUM_ROWS = 2191093 # @param {type:"integer"} 

import tempfile
from pathlib import Path

# Create temporary file to write embeddings to
embeddings_file_path = Path(tempfile.mkdtemp())

print(f"Embeddings directory: {embeddings_file_path}")

#========================================================

import gc
import json

BQ_NUM_ROWS = int(BQ_NUM_ROWS) #上記のBIGQUERYで確認・取得した更新・追加対象の行数
BQ_CHUNK_SIZE = 20000 #20000行ずつ処理/json出力
BQ_NUM_CHUNKS = math.ceil(BQ_NUM_ROWS / BQ_CHUNK_SIZE)

START_CHUNK = 0

# Create a rate limit of 300 requests per minute. Adjust this depending on your quota.
API_CALLS_PER_SECOND = 600 / 60
# According to the docs, each request can process 5 instances per request
ITEMS_PER_REQUEST = 5

# Loop through each generated dataframe, convert
for i, df in tqdm(
    enumerate(
        query_bigquery_chunks(
            max_rows=BQ_NUM_ROWS, rows_per_chunk=BQ_CHUNK_SIZE, start_chunk=START_CHUNK
        )
    ),
    total=BQ_NUM_CHUNKS - START_CHUNK,
    position=-1,
    desc="Chunk of rows from BigQuery",
):
    # Create a unique output file for each chunk
    chunk_path = embeddings_file_path.joinpath(
        f"{embeddings_file_path.stem}_{i+START_CHUNK}.json"
    )
    with open(chunk_path, "a") as f:
        id_chunk = df.id

        #print(df)

        # Convert batch to embeddings
        is_successful, question_chunk_embeddings = encode_text_to_embedding_batched(
            sentences=df["description"].tolist(),
            api_calls_per_second=API_CALLS_PER_SECOND,
            batch_size=ITEMS_PER_REQUEST,
        )


        # Append to file
        embeddings_formatted = [
            json.dumps(
                {
                    "id": str(id),
                    "embedding": [str(value) for value in embedding],
                }
            )
            + "\n"
            for id, embedding in zip(id_chunk[is_successful], question_chunk_embeddings)
        ]
        f.writelines(embeddings_formatted)

        # Delete the DataFrame and any other large data structures
        del df
        gc.collect()

  • できたjsonファイル(110個)をGCSにアップロードします。

! gsutil -m cp -r {embeddings_file_path}/* {remote_folder}


(3)json経由でBigQueryに入れる

  •  BQのschemaを作っておけば、以下のコマンドで全部BQに入れてくれます。idが最初のfamily_idに対応します。

!bq --location=US load --source_format=NEWLINE_DELIMITED_JSON {dataset}.{table} gs://***/20240121*.json schema.json




(4)pineconeでベクトルデータベースに投入

  • BigQueryの内積計算クエリで類似文章も取れますが、費用と速度から、pinecone serverlessにしました。pinecone serverlessは今年pineconeに追加されたサービスで、従来のpineconeのベクトルデータベースよりも速く安いのが売りです。

  • pineconeの画面でベクトルデータベースを作っておけば、下記のようにメインは数行で投入できます。


!pip install pinecone-client -q

#colaboratory用
from google.colab import userdata
PINECONE_API_KEY = userdata.get('PINECONE_API_KEY')

from pinecone import Pinecone
pc = Pinecone(api_key=PINECONE_API_KEY)
index = pc.Index('patent-abst')

  • frameは、上記で作成したテーブルデータをそのまま取ってきたものです。

index.upsert_from_dataframe(frame)




  • なんか件数が減ってしまいましたが、後でUPSERTできなかったものは再実行しないとです。





3.試し

  • 入力文章を(textembedding-gecko-multilingualで)ベクトル表現に変更して、そのベクトル表現でpineconeを検索します。ライブラリなどは上記のものをそのまま使えます。

import vertexai
vertexai.init(project=PROJECT_ID, location=LOCATION)
from vertexai.preview.language_models import TextEmbeddingModel
model = TextEmbeddingModel.from_pretrained("textembedding-gecko-multilingual@latest")

def encode_texts_to_embeddings(sentences: List[str]) -> List[Optional[List[float]]]:
    try:
        embeddings = model.get_embeddings(sentences)
        return [embedding.values for embedding in embeddings]
    except Exception:
        return [None for _ in range(len(sentences))]



  • 上記の関数で文章をベクトル化します。リストで帰ってくるのでvec_list[0]がほしいベクトルです。

text_list = ["雨雲が近づくのを検知して傘を開く"]
vec_list = encode_texts_to_embeddings(text_list)

 こんな感じにでるので、これを使って検索します。


from pinecone import Pinecone
pc = Pinecone(api_key=PINECONE_API_KEY)
index = pc.Index('patent-abst')
index.query(vector=vec_list[0],top_k=10)


  • 数秒で検索結果が出ます。idはfamily_idです。

{'matches': [{'id': '83063341', 'score': 0.831743479, 'values': []},
             {'id': '81855280', 'score': 0.818136454, 'values': []},
             {'id': '74670712', 'score': 0.811068118, 'values': []},
             {'id': '81324479', 'score': 0.810769737, 'values': []},
             {'id': '83721406', 'score': 0.808151245, 'values': []},
             {'id': '81845510', 'score': 0.805805, 'values': []},
             {'id': '85415761', 'score': 0.798141241, 'values': []},
             {'id': '82657703', 'score': 0.796586096, 'values': []},
             {'id': '79908436', 'score': 0.796158314, 'values': []},
             {'id': '85600662', 'score': 0.795632243, 'values': []}],
 'namespace': '',
 'usage': {'read_units': 10}}


上位3位はこちら。①③がイメージしていたのに近いです。

①id(family_id):83063341:TWM628300U



②id(family_id):81855280:JP3237832U


③id(family_id):74670712:TWM604947U



4.最後に

  • 多言語を吸収してくれるembeddingnモデルのおかげですが、結構よい感じにmltilingualな検索をしてくれるなと思いました。今回のトータルの費用は6000円(embedding3000円、pineconeへの登録で2000円)くらいだったので、隙を見て中国文献も入れようと思います。

  • vertexAIで、画像や動画なども含めて検索するようにできればと作成中です。

閲覧数:212回0件のコメント

最新記事

すべて表示

Comments


bottom of page