はじめに(背景)
なぜだ?!なぜ、Neo4J+NeoDashのQiita記事がないんだ?ないんだったら、作るか。しかも今作れば、第一人者になれるわ(2024年4月現在)。そういうわけで、作ってみましょう。
読者の中には、Qiita記事を書く時間がないけど、Neo4J現役バリバリで使っている人もいるだろう。使っているひとを尊敬する。筆者はNeo4Jの存在は以前から知ってはいたが、そのデータ投入がめんどくさくて使う気になれなかったひとりだ。
しかし、neo4jのグラフデータめんどくさがりに強い味方が現れた。大規模言語モデルだ!
まずは、こちらの記事を見てほしい。
(「要約」の抜粋翻訳)
- オープンソースのLLMを微調整することで、OpenAIへの依存から脱却することができます。
- このブログ記事を書いている間、GPUメモリの問題により、20Bモデルを除いて複数のモデルをテストしました。モデルを微調整して、運用環境に十分な Cypher ステートメントを生成できると自信を持って言えます。
これだ!これを我々ものぐさ民は待っていたのだ!
そして、同志たちよ、これも読んでくれたまえ。ちょっと複雑な文脈を扱おうとしているようだが、codellama-13bでやろうとしている例だ。codellama-13b単体では心もとなく、プロンプトに工夫が必要だったそうだ。
だが、2024年春となったいま、langchainがプロンプトも引き受けてCypherしてくれる!
これで、我々は超長文を理解しようとするとき、Neo4Jデータベースを作る仕事はLLMに任せたうえでNeo4Jのグラフインターフェースをいじって文章を理解することができると思わないか?! 思うよね!
筆者はたまたま、LocalAI (https://localai.io/)で構築したOpenAI互換API利用環境を持っている。これを使えば、自前でしかも、Neo4jのインターフェースを閲覧専用公開してほかの人にも見せてみようと思わないか?! 思うよね!
概要
以上の背景を持つこの投稿は、つぎのような作業の記録となります。
(1)Neo4JのDBを立ち上げる
(2)Neo4JにLangchainを使ってデータを投入する
(3)NeoDashを立ち上げる
(4)NeoDashの公開設定を行う
なお、本件取り扱いは、個人的もしくは小規模人数での情報共有目的のNeoDash環境構築を目的としたものであるので、Neo4Jのフリーライセンス利用範囲であります。
また、LocalAI利用環境の構築方法については、今回は書きません。すでに環境ができているものとして記述しています。
もうひとつ、Neo4jのセキュリティ設定については今回は書きません。
目的
個人的もしくは小規模人数での情報共有目的のNeoDash環境構築を目的とします。
手順
実行環境を docker compose する
実行環境を、わざわざjupyterhubで作るという変態的初手を踏む。jupyterhub については、公式のgithubから真似して作る。
neo4j, neodash についても公式のdocker構築術をまねる。
そこで、次のようなフォルダ構成とし、 docker-compose.yml を配置するのはどうだろう。
-(project root)
|--- docker-compose.yml
|--- README.md
|
(jupyterhub)
| |--- config_token.py
| |--- Dockerfile.jupyterhub
| |--- jupyterhub_config.py
|
(neo4j)
services:
neo4j:
image: neo4j:latest
environment:
- NEO4J_AUTH=none
- NEO4J_PLUGINS=["genai", "graphql", "graph-algorithms", "graph-data-science","apoc", "apoc-extended", "n10s"]
ports:
- 7474:7474
- 7687:7687
networks:
- jupyterhub-network
- neo4j-network
volumes:
- ./neo4j/data:/data
- ./neo4j/logs:/logs
- ./neo4j/conf:/conf
neodash:
depends_on:
- neo4j
image: neo4jlabs/neodash:latest
ports:
- 5005:5005
networks:
- neo4j-network
hub:
build:
context: ./jupyterhub
dockerfile: Dockerfile.jupyterhub
args:
JUPYTERHUB_VERSION: latest
restart: always
image: jupyterhub
container_name: jupyterhub
networks:
- jupyterhub-network
volumes:
# The JupyterHub configuration file
- "./jupyterhub/jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py:ro"
# Bind Docker socket on the host so we can connect to the daemon from
# within the container
- "/var/run/docker.sock:/var/run/docker.sock:rw"
# Bind Docker volume on host for JupyterHub database and cookie secrets
- "jupyterhub-data:/data"
- "./example/requirements.txt:/home/jovyan/requirements.txt"
- "./example/prototype.ipynb:/home/jovyan/prototype.ipynb"
ports:
- "8000:8000"
environment:
# This username will be a JupyterHub admin
JUPYTERHUB_ADMIN: admin
# All containers will join this network
DOCKER_NETWORK_NAME: jupyterhub-network
# JupyterHub will spawn this Notebook image for users
DOCKER_NOTEBOOK_IMAGE: quay.io/jupyter/base-notebook:latest
# Notebook directory inside user image
DOCKER_NOTEBOOK_DIR: /home/jovyan/work
volumes:
jupyterhub-data:
networks:
jupyterhub-network:
name: jupyterhub-network
neo4j-network:
name: neo4j-network
neo4j にモリモリでプラグインをぶっこんだが、ライセンスがあわないものは動かない。
jupyterhub について公式のとおりだが、docker.sockがちゃんとアクセスできているのかわからない。今回はそこは突っ込まないで進める。
docker compose up
dockerが、neo4j と neodash と jupterhub を起動する。
jupyter hub にログイン
頃合いをみて、 ブラウザで localhost:8000 にアクセスして、jupyterhub のサインアップを行おう。
jupyterhub 公式にも説明のあったとおり、あらかじめ登録されているユーザーはない。だが、"admin"というユーザーを作るとそれはadminユーザーになるとのこと。
画面左下の sign up のリンクを押して、サインアップ画面でユーザー名「admin」、パスワードは好みで設定する。
sign up ができたら、続けて、jovyanユーザーを作っておくとよいかもしれない。jovyanユーザーは、公式が設定例ユーザーに用意しているユーザーで、コンテナの中のOS上に存在しているユーザーでもある。
ユーザーを作ったらlogin リンクを押してログインに戻ろう。
作った「admin」ユーザーでログインしてみて、さっそくfileメニューを選んでみる。hub control panel でユーザー管理できたりする。
admin ユーザーではログアウトして、jovyanユーザーでログインしなおして以下、操作してみた。
langchain をインストール
jovyanユーザーでログインした筆者は、早速、requirements.txtファイルと、neo4j_examleディレクトリを作成した。今回作成したdocker-compose では、作成したファイルを、永続ボリュームに保存する。作成したファイル類はdockerを再起動しても残るようになっている。
Launchar の Other メニューから TextFileを選んで作成開始。
langchain
langchain-community
langchain-openai
langchain-experimental
neo4j
wikipedia
tiktoken
yfiles_jupyter_graphs
numpy
pandas
ファイルを作成、保存できたら、 Launcher で Terminal を起動。次のコマンドを実行。
mkdir neo4j_example
pip install -U -r requirements.txt
インストールの成功を見届けたら、Terminalを閉じる。
neo4j にデータを投入する
neo4j_exampleディレクトリに、ノートブックを一つ作り、そこにneo4j にデータを投入するスクリプトを書いてみよう。ノートブック名は、テキトーに pototype.ipynb とした。
記述内容は、こちらの先達のものを参考に。
from langchain_core.runnables import (
RunnableBranch,
RunnableLambda,
RunnableParallel,
RunnablePassthrough,
)
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.prompts.prompt import PromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from typing import Tuple, List, Optional
from langchain_core.messages import AIMessage, HumanMessage
from langchain_core.output_parsers import StrOutputParser
import os
from langchain_community.graphs import Neo4jGraph
from langchain.document_loaders import WikipediaLoader
from langchain.text_splitter import TokenTextSplitter
from langchain_openai import ChatOpenAI
from langchain_experimental.graph_transformers import LLMGraphTransformer
from neo4j import GraphDatabase
from yfiles_jupyter_graphs import GraphWidget
from langchain_community.vectorstores import Neo4jVector
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores.neo4j_vector import remove_lucene_chars
from langchain_core.runnables import ConfigurableField, RunnableParallel, RunnablePassthrough
try:
import google.colab
from google.colab import output
output.enable_custom_widget_manager()
except:
pass
langchain 関連のモジュールの読み込みがうまくできたら、次は、「投入データのもとになる長文」のデータ化だ。
langchain の WikipediaLoaderで、エリザベス一世の検索結果(https://en.wikipedia.org/wiki/Elizabeth_I)を取得する。英語版 Wikipedia を参照しているはずだ。
# Read the wikipedia article
raw_documents = WikipediaLoader(query="Elizabeth I").load()
# Define chunking strategy
text_splitter = TokenTextSplitter(chunk_size=512, chunk_overlap=24)
documents = text_splitter.split_documents(raw_documents[:3])
ここ、1分強時間かかるけど、Wikipediaのあの長文を自動でデータ化してくれるんだから、考えたらコスパヨシ。
次は、LLMのセットアップ。
ここは人によると思う。筆者はLocalAIがあるのでllm_server_urlにそのURLを指定するし、自前のLLMモデル名を指定する。LocalAIでなく、OpenAI API を使う人は、OpenAI環境情報をあてはめたらよい。
llm_server_url = "http://localhost:8080"
model_name="gpt-3.5-turbo"
llm=ChatOpenAI(temperature=0.01, model_name=model_name, openai_api_base=llm_server_url, openai_api_key="sk-xkkkkkkkkkkkkkkkkkkkkkkkkkk")
次に、先のWikipedia長文データをグラフデータに!これだ!これだよ!
llm_transformer = LLMGraphTransformer(llm=llm)
graph_documents = llm_transformer.convert_to_graph_documents(documents)
これは、2分ほどかかる。エラーなく終了するのが待ち遠しい!
環境変数設定して、「Neo4jGraph」クラスインスタンスを生成。
docker で起動してある Neo4j のユーザー名は neo4j だが、パスワードは設定していない。だが、この環境変数設定場面で何らかの文字列を持たせていないと、インスタンスの生成がエラーになる。適当な文字列を書いておく。
os.environ["NEO4J_URI"] = "bolt://neo4j:7687"
os.environ["NEO4J_USERNAME"] = "neo4j"
os.environ["NEO4J_PASSWORD"] = "everything"
graph = Neo4jGraph()
ここまでエラーなく動作した時点で、ワクワクする! わくわくしながら、次の項目へ。
Neo4J サーバーへデータ投入だ!
graph.add_graph_documents(
graph_documents,
baseEntityLabel=True,
include_source=True
)
これもエラーなく終わったら、NeoDashでみよう!ひゃっほー!
NeoDash にアクセスする。
ブラウザで http://localhost:5005 にアクセスする。
New Dashboard をクリックして、Create New Dashboad を Yes だ!
なにも設定してないので、そのまま「 Connect 」
真ん中の記号みたいのを押す・・・
右上の3点を押す・・・
Typeを選んで、キャレットの点滅している欄に、Cypherでクエリを書くと、それがビジュアライズされる!すげぇ!
サンプルとして、エリザベス一世の人間関係を調べてみたいな、と、いうわけで、TypeにGraphを選び、次のクエリを書いて、右上の丸に右向き三角なアイコンを押す!
MATCH (n:Person) RETURN n LIMIT 25
真ん中付近で、マウススクロールさせると拡大縮小されるので、なんとかElizabethIを見つけて右クリック。
Expandで、なんかいっちゃん大きい数字の __Entity_Person を押すと・・・
キャー!もう、ナニコレ、かわいい(爆)
しかし小っちゃくてみにくいわー(笑)
保存するまえに一工夫。次に開くときEditモードじゃなくて読み取りモードにしておきましょう。右上のギヤマークアイコンを押して、表示されるモーダルのメニューで Editable をオフ。
画面左、サイドメニューを開き、(Draft)になっている現在のダッシュボード名に名前をつけて、その横の3点リーダをクリック。Saveメニューを選んで、保存しませう。
書き込み権限があるかー?とか聞かれるけど、今回はある。
左サイドメニューが (Draft)でなくなり色も変わって、3点リーダのメニュー項目も変わっている。
Neo4j Dashboard を publish !
さて、このメニュー項目から「Share」を選ぶと、ダイアログが表示される。
今回作成したダッシュボードは、自前のDocker上にあるので、「Self-hosted」を選ぶ。ほかの人にはエディットされない状態でShareしたいので、「Hide Editor UI」を選ぶ。
そうすると、下にリンクが表示されるので、これをシェアすればよい!
・・・と、言っても、シェアリンクとしてはDockerで作った localhost アドレスになっているから、このままじゃほかのPCからは見えない。
ちなみにこのリンクを開くとNeoDashからNeo4j dashboardを開くよ~、みたいなメッセージが表示される。
その次には、Neo4j へのログイン情報確認ダイアログが表示される。実際に使うときは、ちゃんとユーザー名とか設定して、セキュリティも確保しないとな。
シェアできた・・・けど
今回は、セキュリティ関連の設定を一切おこなわず、Docker で NeoDash の共有機能を表示した。信頼できる顔見知りには見せてもいいけど、一般公開ってわけにはいかないですな。そのあたりは、また別のきかいに・・・
Langchainは、Graphデータを用いた探索型QA(RetrievalQA)システム構築もできるらしい。
Neo4jとLangchainで、だまだアレコレいじってみるのも面白そう。
終わりに(結論)
すごいや、Graphデータベース Neo4j と そのダッシュボード作成ツール NeoDash。
そして Neo4j へのデータ投入を容易にしてくれた Langchain もすげえ。
すげぇって感想で終わるって・・・結局自己満足だったってことやな。
みせびらかすってそんなもんや。(爆)