(2019-07-15) いつの間にかライブラリ名から新しくなってしまい、以下の記事が役にたたなくなってしまいました。新しい記事Jupyter NotebookからWatson Discoveryを操作する(2019-07-15最新版)を書き直したので、今後はこちらを参照して下さい。
(2018-07-01) シドニーのサイトの場合の認証方式追記
はじめに
リリースノートに記載のとおり、2018-06-25にWatson Discoveryでようやく日本語のフルサポート(NLUによるアノテーション機能等が追加された)が行われました。
このタイミングで、日本語文書のテストをしてみたく、試すときに使ったJupyterNotebookを連携します。
試しに10000行のデータでやってみましたが、全件問題なくアップロードできました。
(10000行=10000件のデータ量はライトアカウントの場合上限を超えています。自分の環境でこのテストを行う場合、スタンダードライセンスが必要ですのでご注意下さい。)
Jupyternotebookのリンクはこちら
前提
Jupyter Notebook環境
Jupyter NotebookはWatson Studioのものを使っています。
Watson Develpoer CloudのライブラリはNotebookから
!pip install watson-developer-cloud
としてもいいのですが、Watson StudioのExvironment設定に以下の記述をして対応しました。
# Please add conda channels here
channels:
- defaults
# Please add conda packages here
dependencies:
# Please add pip packages here
# To add pip packages, please comment out the next line
- pip:
- watson-developer-cloud==1.4.0
インスタンスの地域
(2018-07-01)
デフォルトの地域である「シドニー」でインスタンスを作ると、認証の方法が従来から変更になります。
この記事では従来型の「米国南部」ケースと両方のパターンを記載しました。
対象データ
以下の例では、こんな形式のCSVファイルを読み込んでアップロードすることを想定しています。
項目名、項目数はコードを変えれば変更可能な作りになっています。
手順解説
コレクション作成まで
Level2対応のタイミングでUIから日本語コレクションを作れるのかと思いきや、まだ多少時間がかかるようです。
なので、現時点は従来通りAPI呼出しでコレクション作成まで行う必要があります。
credentail情報設定
管理コンソールのcredentail情報をコピーします(アイコンクリックで可能)。
サイトにより情報は異なります。
【米国南部】
#
credencial = {
"url": "https://gateway.watsonplatform.net/discovery/api",
"username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"password": "xxxxxxxxxxxx"
}
【シドニー】
credencial = {
"apikey": "xxxxxx",
"iam_apikey_description": "xxxxxx",
"iam_apikey_name": "xxxxxx",
"iam_role_crn": "xxxxxx",
"iam_serviceid_crn": "xxxxxx",
"url": "https://gateway-syd.watsonplatform.net/discovery/api"
}
ライブラリ読み込み
このステップもサイトにより異なります。
この段階では、単に変数の定義をしているだけで、実際にAPIを発行するのはもう一つ先のステップになるので注意して下さい。
【米国南部】
import json
from watson_developer_cloud import DiscoveryV1
# In the constructor, letting the SDK manage the IAM token
discovery = DiscoveryV1(version = '2018-03-05',
username = credencial['username'],
password = credencial['password'])
【シドニー】
import json
from watson_developer_cloud import DiscoveryV1
discovery = DiscoveryV1(version='2018-03-05',
iam_api_key=credencial['apikey'],
url=credencial['url'])
environment_id取得
ここから先の手順はサイトによらず共通になります。
environment_idがすでにあるときはそのIDを取得、ない場合は新規に作成を行うスクリプトです。
environments = discovery.list_environments()['environments']
if len(environments) == 1 :
# Environment作成
response = discovery.create_environment(name="my_environment")
environment_id = response['environment_id']
else :
# 既存env_id取得
environment_id = environments[1]['environment_id']
print('environment_id: ', environment_id)
collection_id取得
environment_id同様、すでにIDがある場合はその値を取得し、ない場合は新規に作成を行います。
collections = discovery.list_collections(environment_id = environment_id)['collections']
if len(collections) == 0:
# Collection作成
col_name = 'JP_TEST'
collection = discovery.create_collection(
environment_id = environment_id,
name = col_name,
language='ja')
collection_id = collection['collection_id']
else:
collection_id = collections[0]['collection_id']
print('collection_id: ', collection_id)
必要であれば、このタイミングでエンリッチ設定の変更を行います。
一度コレクションを日本語で作ってしまえば、エンリッチ設定変更はUIから可能です。
CSVファイル読込み
取り込みたいCSVファイルはあらかじめ、Watson Studioのデータアセットとしてアップロードしておきます。
以下のコードではCSVファイルの先頭行はヘッダ行になっている前提です。
下記のコードは、Watson StudioのJupyter固有のもので、Watson Studioのツールで自動生成したコードに一部手直しをする想定です。
普通にローカルのcsvを読み込むのであれば、普通にpd.read_csv関数呼び出しでOKです。
要は、このセルが終わった段階で、CSVデータがdf_dataに入っていればいいです。
infile = 'data_xxxxxx.csv'
# このセルは、ツール生成のものを一部手で修正します。
import sys
import types
import pandas as pd
from botocore.client import Config
import ibm_boto3
def __iter__(self): return 0
client_xxxxxx = ibm_boto3.client(service_name='s3',
ibm_api_key_id='xxxxxx',
ibm_auth_endpoint="https://iam.ng.bluemix.net/oidc/token",
config=Config(signature_version='oauth'),
endpoint_url='https://s3-api.us-geo.objectstorage.service.networklayer.com')
# Key=のパラメータは変数利用に修正しました
body = client_38bc5a04c29444d093d357a963ea4f0d.get_object(Bucket='xxxxxxx',Key=infile)['Body']
# add missing __iter__ method, so pandas accepts body as file-like object
if not hasattr(body, "__iter__"): body.__iter__ = types.MethodType( __iter__, body )
# DataFrame名は df_dataに修正しました
df_data = pd.read_csv(body)
df_data.head()
読み込んだデータをDiscovey Collectionに書き込み
以下のサンプルコードは、CSVの項目名がpat_id,pat_name,pat_sumamry,pat_text
となっていて、このうちのpat_text
に対してエンリッチをかける前提のものです。
要件に応じて適宜修正をかけて下さい。
いきなり全量をアップロードすると、バグがあったとき面倒なので、
- 最初は数行
- うまくいったら100行程度
- これでうまくいったら全量
の3段階でアップロードすることをお勧めします。
21件以上処理中なのに追加文書登録をするとエラーになるようなので、1件登録する度に現在何件処理中か確認して、危なそうな時はタイマーで待ちを入れるロジックを追加しています。
import os
import time
for i in range(len(df_data)):
item = df_data.iloc[i,:]
data = {}
# 以下はCSVの項目名によって修正して下さい
data['pat_id'] = str(item['pat_id'])
data['pat_name'] = item['pat_name']
data['summury'] = item['pat_summury']
data['text'] = item['pat_text']
# print(data)
filename = data['pat_id'] + '.json'
f = open(filename, 'w')
json.dump(data, f)
f.close()
collection = discovery.get_collection(environment_id, collection_id)
proc_docs = collection['document_counts']['processing']
while True:
if proc_docs < 20:
break
print('busy. waiting..')
time.sleep(10)
collection = discovery.get_collection(environment_id, collection_id)
proc_docs = collection['document_counts']['processing']
# print(json.dumps(collection, indent=2))
with open(filename) as f:
add_doc = discovery.add_document(environment_id, collection_id, file = f)
os.remove(filename)
# print(json.dumps(add_doc, indent=2))
(おまけ1)データ削除用スクリプト
テストでアップした少量データ削除用のスクリプトです。
# 文書件数取得
collection = discovery.get_collection(environment_id, collection_id)
doc_count = collection['document_counts']['available']
results = discovery.query(environment_id, collection_id, return_fields='id', count=doc_count)["results"]
ids = [item["id"] for item in results]
for id in ids:
print('deleting doc: id =' + id)
discovery.delete_document(environment_id, collection_id, id)
(おまけ2) 類似検索
Discoveryでは類似検索も可能です。手順は【マメ知識】Watson Discoveryで類似文書検索を行うに書いておきましたので、参考とされて下さい。