ClojureとPythonの連携と機械学習

機械学習技術 自然言語技術 人工知能技術 デジタルトランスフォーメーション技術 画像処理技術 強化学習技術 確率的生成モデル 深層学習技術 Python Clojure 本ブログのナビ
Clojureとは

Clojureは様々な言語と横断的につながるフレームワークを持っている。たとえばClojureScript(cljs)は、google closureを利用してcljsコードをベアなJavascriptにコンパイルするしくみを持つ(typescript等の現在のAltJavascriptと同様のしくみ)。また同様にCSSも変換する仕組みを持つことから、Clojureだけでフロントエンドを記述することができる。

この仕組みとClojureのベースプラットフォームであるJava((既存のシステムの50〜60%がJavaで構築されている)が持つ様々なライブラリーがClojureでもネイティブに利用可能であること、さらにClojure自身も後述するマイクロサービスに用いられるような優れたフレームワーク(datomicpedestalduct等)も持っていることなどから、特にwebシステム等の構築の分野ではWorld Wide(日本での事例は先進的な一部の企業に限られる)で広く用いられている。

ClojureとPythonやRを連携させた機械学習

それに対して機械学習の領域では、PythonRのような豊富なライブラリーを持つ環境が利用されててほぼデファクトとなっているが、これに対して初期(2007年〜2017年頃)のClojureでは、CやPython、R等と繋げるしくみも開発されていたが、相手先のライブラリを自由に扱えるレベルではなく、最新のアルゴリズムを駆使することにハードルがあった。

これに対して近年(2018年〜)、libPython-cljのようなPython環境と相互に運用可能なフレームワークが現れたり、またJavaやCのライブラリを活用した数学的フレームワークfastmathや、深層学習のフレームワークCortexDeep Diamond等が開発されたりすることで、機械学習へのアプローチが積極的に検討され、Clojureの機械学習のコミュニティとして有名なscicloj.ml等で活発に議論されるようになった。

さらに次世代のAI技術として注目されている第四世代のAI技術は、第三世代の技術である深層学習技術と第一、第二世代の技術である知識・記号推論技術を融合することが提案されており、第一、第二世代の知識・記号推論技術に用いられたLispを祖先に持つClojureが、Pyhton等と融合した機械学習技術を持つことで、単一のプラットフォーム上で第四世代のAI技術を構築できる可能性が期待され、欧米を中心に盛んに検討が行われている。

libpython-cljを使った実装とPythonライブラリのClojureでの活用

今回はこれらのうち、libpython-cljの立ち上げと利用について述べる。

libpython-cljはPython を Clojure に深いレベルで統合することを目指して開発されたもので。これは、まるで Clojure 名前空間であるかのように、Python モジュールをロード/使用できるようにしたいということを意味し、また、Clojure を使用して Python オブジェクトを拡張できるようにすることを目指している。彼らの詳細なビジョンは Clojure Conj 2019 での講演で知ることができる。

詳細の情報はgitのページに掲載されている。exampleのページにはGPT2 text generation from hugging-face, MXNet MNIST classification using the Module API, Pytorch MNIST, Matlib PyPlot, NLTK, SpaCy, Sci SpaCy, Seaborn, UMAP, TRIMAP, Igraph, Leiden, Sklearn, Facebook Prophet, Pygal, Bokeh, OpenCV, psutildiffprivlb, transformers等の主だったpythonライブラリを使った例について述べられている。

具体的な実装について述べる。まずClojureの開発環境立ち上げに関しては以下のページを参照のこと、libpython-cljをM1 macに導入するためには、JDK17を用いる必要がある。現在デフォルトではJDK18が入っているが、いくつかエラーが生じているようであり、libpython-cljの作成者も次の19に対応するからということで、ウェブ上でのerrorQAを見ると、JDK18には対応する意思はないようである。JDK17のインストールは例えばbrewを使う場合は”brew install openjdk@17″でインストールしてパスを通すか、jenvを使ってインストールし、切り替えられるようにする。

次に新規のプロジェクトをleiningenで作成する。

lein new linpython-test01

プロジェクトファイルは以下のようにする。

;; project.clj
(defproject libpython-clj-test01 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [clj-python/libpython-clj "2.018"]
                 ]
  :repl-options {:init-ns libpython-clj-test01.core}
  :jvm-opts ["--add-modules" "jdk.incubator.foreign"
                       "--enable-native-access=ALL-UNNAMED"])

libpython-cljライブラリは最新バージョンは”2.018″となる。また、jvmオプションとして”:jvm-opts [“–add-modules” “jdk.incubator.foreign””–enable-native-access=ALL-UNNAMED”]”を加える必要があることに注意が必要となる。

ClojureのREPL開発は「SublimeText4でのClojureの開発環境」等を参照のこと。

まず名前空間の設定とconfigとしては以下のようになる。

(ns libpython-clj-test01.core
  (:require 
      [libpython-clj2.python :as py :refer [py. py.. py.-]]
      [libpython-clj2.require :refer [require-python]])) 

(py/initialize! :python-executable "/Users/XXX/python3"
                :library-path "/Users/XXX/lib/python3.9")

“py/initialize!…”でpython環境の設定を行う、まず”:python-executable “/Users/xxx/python3″では”which python3″で表示される。動作させるpythonのパスをコピーする。次にライブラリパスである”:library-path “/Users/xxx/lib/python3.9″に関しては、一旦pythonを動作させた後、”import sys””print(sys.path)”でライブラリのパスを確認してコピーする。

まずはHello Worldから、

(py/run-simple-string "print ('Hello World!')")  ;;Hello World!

REPLをかけると、pythonのコードである”print(‘Hello Wolrd!’)”が実行されて、”Hello World!”が出力される。

次にnumpyのインポートと実行を行う。

(require-python '[numpy :as np])   ;ok

(py/from-import numpy average)     ;;#'libpython-clj-test01.core/average

(average [1, 8, 4, 10])            ;;5.75

さらにもう少し高度なライブラリとして、自然言語理解と自然言語生成の汎用アーキテクチャ(BERT、GPT-2など)と何千もの事前学習すみモデルを提供するライブラリであるTransformerモデルの概要とアルゴリズム及び実装例について“でも述べているtransformersを利用してzero-shot-classificationを行なった場合

(require-python '[transformers :bind-ns])

(def classifier (py. transformers "pipeline" "zero-shot-classification"))

(def text "French Toast with egg and bacon in the center with with maple syrup on top. 
           Sprinkle with powdered sugar if desired.")

(def labels ["breakfast" "lunch" "dinner"])

(classifier text labels) ;;{'sequence': 'French Toast with egg and bacon in the center with with maple syrup on top. Sprinkle with powdered sugar if desired.',
                         ;; 'labels': ['breakfast', 'lunch', 'dinner'], 
                         ;;'scores': [0.9893278479576111, 0.00738490978255868, 0.0032872725278139114]}

分類したいテキスト”French Toast with egg and bacon in the center with with maple syrup on top. Sprinkle with powdered sugar if desired.”とカテゴリ[“breakfast” “lunch” “dinner”]を入力して分類すると、元々持っているfacebook/bart-large-mnliモデルを使って計算されて、Breakfastと判定される。

次に説明できる機械学習のツールとして紹介した「lime」の適用(前述のtransformerのデータを説明する)は以下のようになる。

(require-python '[lime.lime_text :as lime])

(require-python 'numpy)

(def explainer (lime/LimeTextExplainer :class_names labels))

(defn predict-probs
  [text]
  (let [result (classifier text labels)
        result-scores (get result "scores")
        result-labels (get result "labels")
        result-map (zipmap result-labels result-scores)]
    (mapv (fn [cn]
            (get result-map cn))
          labels)))

(defn predict-texts
  [texts]
  (println "lime texts are " texts)
  (numpy/array (mapv predict-probs texts)))

(predict-texts [text])

(def exp-result
  (py. explainer "explain_instance" text predict-texts
       :num_features 6
       :num_samples 100))

(py. exp-result "save_to_file" "explanation.html")

結果として、以下のようなものが得られる。

transformaersで分類したいテキスト”French Toast with egg and bacon in the center with with maple syrup on top. Sprinkle with powdered sugar if desired.”とカテゴリ[“breakfast” “lunch” “dinner”]を入力して分類すると”breakfast”の判定結果が出たが、limeでそれらを判定させた場合、単語の中で最も影響が大きかったものが”Toast”であるという結果が出ている。

 

コメント

  1. […] ClojureとPythonの連携と機械学習 […]

  2. […] ClojureとPythonの連携と機械学習 […]

  3. […] Clojure : ClojureはJVM上で動くLISPである為、前述のJavaのライブラリはそのままネィティブに動作させることができ、またJavaのJNIインターフェースを介してCのライブラリを直接利用することもできる。更にマクロ機能等の言語の柔軟性を用いて”ClojureとPythonの連携と機械学習“や、”ClojureとRの連携による統計的学習“などに述べられているようにPythonやRのライブラリも利用することができる。Clojureネイティブの線形代数ライブラリとしては行列を扱う”core.matrix“、”clatrix“、一般的な線形台数を扱う”incanter“などがある。 […]

タイトルとURLをコピーしました