TECH PLAY

Anaconda

イベント

該当するコンテンツが見つかりませんでした

マガジン

該当するコンテンツが見つかりませんでした

技術ブログ

技術を土台にして自分なりのQAエンジニアを目指す本連載、第6回のテーマは「テスト自動化」です。 前回の記事 をご覧いただいた方はご存じだと思いますが、私は文系大学出身で、キャリアのスタートは営業職でした。 実務で、商用のプロダクトコードを書いた経験は、今もありません。 もっと言えば、かつての私は「Pythonの環境構築」をするためだけに、1カ月以上も躊躇して手が動かなくなるような人間でした。当時の上司から「Python興味あるんだったらなんで入れないの?」「やらないってことは興味ないってことじゃん」と言われた記憶があります。 私が上司だったらそんなことは言わないですが、そう思う気持ちはすごくわかります。 当時は本当に何もわからずに、「Anaconda」がいいのか、「仮想環境」がいいのか、公式からインストールできるPythonがいいのか。 そもそもPCにPythonを入れてしまって、壊してしまうかどうかも不安でした。 そんな私が、どのようにしてテスト自動化という領域に自信を持ち、それをQAエンジニアとしての土台に変えていったのか。 今回は、ツールを動かすことの先にある「設計原則」への理解と、そこから得られた視点についてお話しします。 記事一覧:技術を土台にして自分なりのQAエンジニアを組み立てる -あるQAの場合 【第1回】専門性をつなげて、あなたらしいQAエンジニアの像をつくる 【第2回】テストを設計する専門性 【第3回】テストマネジメントはテストマネージャーだけの技術ではない 【第4回】テストプロセス改善:思考の補助線としての専門技術 【第5回】幕間:異業種経験を土台にする 【第6回】テスト自動化:「設計原則」を知り、当たり前の技術にする 本稿におけるテスト自動化 本題に入る前に、この記事で扱う「テスト自動化」について定義しておきます。 一般的にテスト自動化といえば、「ツールを使ってテスト実行を自動化すること」と捉えられがちです。 しかし、AIによるコーディングが当たり前になった現代において、私はより広い意味を定義したいと思います。 「テストという活動を構造化し、実行可能な『ソフトウェアシステム』として設計・構築・運用する技術」 テストにおいて、単にスクリプトを書くことと、システムとして構築することは似て非なるものです。 前者は時に手順の翻訳となってしまいますが、後者にはアーキテクチャが必要で、保守性への配慮も必要であり、なにより 「テストそのものへの深い理解」 が必要です。 かつてはテスト自動化スクリプトを書くだけでも立派な「テスト自動化エンジニア」でした。  しかし、2026年1月現在、AIはスクリプトを書くことはできますが、プロジェクトのコンテキストに適した「テストシステム」の青写真を人間の補助なしに、プロジェクトに最適化された形で描くことはできません。 この 「テストシステムを設計する技術」 こそが、本稿で伝えたいテスト自動化の本質です。 ツールを通して「普遍的な課題」を学ぶ 私がテスト自動化を学び始めた当初、関心は「どうやって動かすか」というHowにありました。 当初は書いたコードがすぐ壊れる辛さや、朝になったら自動テストが動いてない悲しみを味わっていたことを覚えています。そこから、Page Object ModelやCIの学習を深め、Playwrightなどのモダンなツールの設計思想に触れるにつれて、視点が変わっていきました。 自動化ツールやデザインパターンは、単なる便利な機能の寄せ集めではありません。 それらは、 テスト活動が抱える「普遍的な課題」への解決方法 そのものでした。 例えば、WebのE2Eテストでは「待機処理」が頻繁に課題になります。 これは、テスト実行環境やネットワークの「不確実性」といかに向き合うかという、Webの自動テストにおける難しいテーマです。 また、UI変更のたびにテスト修正に追われた経験や、Flakyなテストへの対応は、まさに「保守性」の課題そのものでした。 優れたツールには、こうした課題に対する一貫した問題意識や思想が込められています。 「なぜ、この機能があるのか?」「なぜ、この設計なのか?」 その背景にある思想を理解することは、単にツールの使い方を覚えるだけでなく、テストそのものに対する解像度を一気に高めてくれます。 自動化技術を学ぶことは、コーディングスキルを磨くだけでなく、こうしたテストの構造的な課題を深く理解するプロセスでもあります。 これはE2Eテストツールを通じて、自分ごととなる課題と、それをソフトウェアで解決するということをリアルに感じた瞬間でした。 「設計原則」が技術的な自信をくれた プロダクトコードを書いたことのない私が、技術的な議論に加われるようになった最大の要因は、「設計原則」を知ったことです。 自動テストを書いていくうちに、「動けばいい」だけのコードに限界を感じ、気がつきました。自動テストのコードもまた、ソフトウェアだということです。 そこにはソフトウェア設計の原理原則が適用されます。特に重要だったのが「関心の分離」や「単一責任の原則」といった概念です。 テストコードの良し悪しを言語化できる これらの原則を意識するようになったことで、私はコードを「なんとなく動く」ではなく「構造」や「意味」で捉えられるようになりました。 例えば、表面的な理解しかしていない私では、生成AIや他者が書いたテストコードに対し、「動いているからOK」としか言えなかったと思います。 しかし今は、違和感を自分自身で言語化し、「テストの保守性」や「意図」という観点からレビューができるようになりました。 「このアサーションはこのテストで本当に確認したい内容でしょうか?」 「このコードは分離して共通化することが可能ではないでしょうか?」 「ツールが目指す方向性に合っているか」「良い構造か、将来の変更に耐えうるか」を判断するための視点は、ツールの思想や、設計原則を学ぶことで養えます。 この視点を持てたことが、私の技術的な自信の源泉となりました。 自動化を「当たり前の選択肢」にする 設計原則を知り、技術的な見通しが立つようになると、テスト自動化に対する心理的なハードルが消え去りました。 そして、テスト自動化は特別な領域ではなく、「当たり前の選択肢」の一つになっていることに気がつきました。 「選択できる」という強み 今の私は、簡単なスクリプトであれば構成を考えてサッと自分で書くことができます。(今では生成AIを使いますが) あるいは、複雑なテストであっても、その構造を読み解き、保守のリスクを見積もることができます。 重要なのは「全てを自動化すること」ではありません。 「ここは手動でやるべきか、自動化すべきか」という問いに対し、技術的な裏付けを持った上で自信を持って検討できる状態にあることです。 自動化もできるし、手動もできる。「今回はこちらがベターな選択だ」と根拠を持って説明できること。 これが、QAエンジニアとしての幅を広げていると考えています。 専門性の組み合わせ 最後に、この技術が他の専門性とどう結びつくか触れておきます。 テスト設計との組み合わせ 自動化の構造(アーキテクチャ)を理解することで、テスト設計の質が変わります。 まず私が強く感じているのは、 自動テストを書くことによってテストケースの性質を理解できるようになったことです。 自動化しやすいテストケースは、往々にして「前提条件が明確」で「責務が単一」な、人間にとっても分かりやすいテストケースです。 「関心の分離」という思考の補助線は、自動・手動を問わず、堅牢なテスト設計を行うための強力な武器になります。 テストマネジメント テストマネジメントにおいては、ROIの判断や戦略的に自動化を取り入れるかどうかの判断が、より精緻になると考えています。自動化コードの「保守コスト」や「技術的負債」のリスクを実体験に基づいて理解しているため、過度な期待も悲観もせず、 プロジェクトの状況に合わせて適切なタイミングで自動化という選択肢を選べるようになりました。 おわりに かつての私のように、「文系だし」「環境構築すら怖い」と尻込みしている方も多いかもしれません。 私にとって、テスト自動化を学ぶことは、ある意味で「プログラマーになること」と同義でした。 しかし、そのハードルは、想像していたよりもずっと低いものでした。 そしてそのハードルを飛び越えることによって、プログラマーとして「プロ」であることの困難さと、その凄みを目の当たりにしました。 勇気を出してその一歩を踏み出してみれば、そこにはシステムが動く仕組みや、先人たちが築き上げたソフトウェア設計や原則が、複雑な現実を生き抜くための「道しるべ」となってくれるはずです。 テスト自動化を学ぶことは、その「道しるべ」を発見する最高のきっかけになるはずです。 その小さな一歩が、あなたのQAエンジニアとしての強固な土台となると考えています。 【連載】技術を土台にして自分なりのQAエンジニアを組み立てる -あるQAの場合 【第1回】専門性をつなげて、あなたらしいQAエンジニアの像をつくる 【第2回】テストを設計する専門性 【第3回】テストマネジメントはテストマネージャーだけの技術ではない 【第4回】テストプロセス改善:思考の補助線としての専門技術 【第5回】幕間:異業種経験を土台にする 【第6回】テスト自動化:「設計原則」を知り、当たり前の技術にする The post 【第6回】テスト自動化:「設計原則」を知り、当たり前の技術にする first appeared on Sqripts .
はじめに 皆さん、こんにちは!AIシステム部のS.Tです。 開発をする上できちんと考えなければいけないパス問題。 今回はPythonのパス解決法を考えてみました。 パス解決とは? 例えば次の例文を考えます。 import hashlibimport pandas このとき、importされる各モジュールは、実際にはディスクのどこかのファイルとして保存されています。 どこのファイルから読み込まれたかは、 __file__ 属性を参照すれば確認できます。 print(hashlib.__file__)# => /home/xxxxx/.pyenv/versions/X.Y.Z/lib/pythonX.Y/hashlib.py# (おおもとのpythonのビルトインライブラリが読み込まれた)print(pandas.__file__)# => /home/xxxxx/projects/yyyyy/.venv/lib/pythonX.Y/site-packages/pandas/__init__.py# (pipenvによってプロジェクトルートに生成された仮想Python環境) この import <ライブラリ名> と呼び出したときに、実際にはそのライブラリをどこから読み込むかを決める作業を「 パス解決 (Path Resolution)」といいます。 パスには解決順がある Pythonは、ライブラリをインポートしたときに、あるきまった順番で、そのライブラリがないかを探しに行きます。 その順番は、sys.pathの値を見ると確認できます。 import syssys.path[ '', # カレントディレクトリ '/home/xxxxx/.pyenv/versions/X.Y.Z/lib/pythonXY.zip', '/home/xxxxx/.pyenv/versions/X.Y.Z/lib/pythonX.Y', '/home/xxxxx/.pyenv/versions/X.Y.Z/lib/pythonX.Y/lib-dynload', '/home/xxxxx/projects/my-project/.venv/lib/pythonX.Y/site-packages'] 上記の例では、例えば import pandas と実行したときは、このリストの上から順番にpandasがあるかどうかを探しに行きます。 解決順の決まり方 このsys.pathですが、Pythonを実行する環境や、Pythonをインストールした場所によって変わります。では、どのようにしてその値が決まるかというと、Python起動時に、次の順番でsys.pathに先頭から値が追加されていきます。 ① カレントディレクトリ(Pythonを起動したときに居た場所) + ② 環境変数PYTHONPATHの値 + ③ インストール場所に依存するパス + ④ .pthによって追加されるパス ① カレントディレクトリ これはもうそのままで、無条件で先頭に追加されます。つまり、import hogehogeと実行したときに、まずはカレントディレクトリにhogehogeがないか確認しに行きます。 ② 環境変数PYTHONPATHの値 Pythonを起動する際に、環境変数PYTHONPATHにディレクトリのパスを設定しておくと、その場所を解決先の候補に加えることができます。加えられる位置は、①のカレントディレクトリの後です。 $ export PYTHONPATH=/home/xxxxxx/my-packages$ python>>> import sys>>> sys.path['', '/home/xxxxxx/my-packages', '/home/xxxx/.pyenv/versions/X.Y.Z/lib/pythonXY.zip', ....(以下略)] また、Linux系OSの場合は:(コロン)で、Windowsの場合は;(セミコロン)で区切ることで複数のパスを解決先の候補に加えることができます。 $ export PYTHONPATH=/home/xxxxxx/my-packages:/home/xxxxxx/my-tools$ python>>> import sys>>> sys.path['', '/home/xxxxxx/my-packages', '/home/xxxxxx/my-tools', '/home/xxxx/.pyenv/versions/X.Y.Z/lib/pythonXY.zip', ....(以下略)] 個人開発したツールがどこか別の場所にあって、それを使いたい場合は、このオプションを使いましょう。 ③ インストール場所に依存するパス これはPythonをOSにインストールした際に、どのパスにインストールしたかによって変わる値です。実行するPythonのバージョンや、Anacondaなどのリポジトリ、仮想環境の違いによって決まります。 具体的には、sys.prefixが、今実行しているPythonのインストールされているディレクトリで、その値をもとに、次のようなパスが追加されていきます。 <sys.prefixの値>/lib/pythonX.Y # 主要ライブラリ(`os`とか`sys`とか)<sys.prefixの値>/lib/pythonX.Y/lib-dynload # Cライブラリ<sys.prefixの値>/lib/pythonX.Y/site-packages # サードパーティライブラリ ④ .pthによって追加されるパス これはあまり使うことはないと思いますが、site-packageディレクトリの子ディレクトリにもパスを通したい場合に、site-packageディレクトリ内に拡張子が.pthのファイルを置き、そこにパスを通したい子ディレクトリを書いておくと、それも解決先の候補に加えることができるというものです。 試しに、hogeというディレクトリをsite-packagesに追加し、そこにパスを通してみます。 $ echo "hoge" > /home/xxxx/.pyenv/versions/X.Y.Z/lib/pythonX.Y/site-packages/mypath.pth$ mkdir /home/xxxx/.pyenv/versions/X.Y.Z/lib/pythonX.Y/site-packages/hoge$ python>>> import sys>>> sys.path['', '..(中略)..', '/home/xxxxxx/.pyenv/versions/X.Y.Z/lib/pythonX.Y/site-packages', '/home/xxxxxx/.pyenv/versions/X.Y.Z/lib/pythonX.Y/site-packages/hoge'] 上記の通り、作成したhogeディレクトリにパスが通りました。 ちなみに、.pthに追加したディレクトリが存在しない場合、sys.pathには追加されないようです。ちょっとしたやさしさを感じます。 まとめ というわけで、パス解決について今回は詳しく見てみました。 「あれ?正しくimportされない・・」と思った時に、どう対処すればよいかを考えるとき、ここで解説したことが役に立ってくれればと思います。 おまけ 当社のキャリアアドバイザーが「WEB事業会社に興味はあるけど、何から始めるべきかわからない」「転職は考えていないけど、今の環境が今後に活きるのかわからない」など、今後のために情報収集したい、という方に少しでも参考になる情報をまとめています。良ければご覧ください! ###card_post_id=2375###
はじめに こんにちは、ITソリューション部(デジタルテクノロジー戦略本部)のS.Dです。 今回は将棋AIを自分で作ってみたのでエンジニアブログで紹介します!目標は、 棋譜データを学習させて、実際に対局できる(ルールに違反しない)ようにさせること とします。 環境は以下で構築しました。 環境 バージョン OS Windows Pro 64bit CPU Ryzen 3900X GPU Geforce RTX2060Super Python 3.8.5 CUDA 10.1 cuDNN 7.6.5 Chainer 7.7.0 python-shogi 1.0.10 この記事ではPytorch ではなく Chainerを使用します。Pytochでも、構築可能です。また、別の機会があればPytorchで実装したものもご紹介できればと思います。 §0. 環境構築 この章では、環境構築をします。 0.1 Visual Studio ビルドツール 2015のインストール 現状の最新のVisual Studioは、2019ですが、2019では正常に動作しない報告が多いので2015をインストールします。 Microsoft Build Tools 2015 Update 3 から、インストーラをダウンロードできます。 0.2 CUDAのインストール NVIDIA からインストーラをダウンロードできます。 2020.11.24時点で、最新バージョンは11.1です。 しかし、使用するソフトウェアに対応したバージョンをインストールします。 現状TensorFlowやChainerなどが対応したバージョンは、10.0, 10.1, (10.2)です。 私は、10.1をインストールしました。 0.3 cuDNNのインストール NVIDIA から、zipファイルをダウンロードできます。 2020.11.24時点で、最新バージョンは8.0.5です。 しかし、使用するソフトウェアに対応したバージョンをダウンロードします。 現状TensorFlowやChainerなどが対応したバージョンは、7.6以前(8.0以降もたぶん大丈夫)です。 私は、7.6.5で動かしています。 ファイルを解凍したら、NVIDIA GPU Computing Toolkitにファイルをコピーします 私の場合は、 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1 にファイルをコピーしました。 画像にある、ファイルとフォルダをコピーして、上のフォルダに移します。 0.4 Chainerのインストール Chainer をインストールします。 Chainerは、機械学習のPythonフレームワークです。日本企業の株式会社Preferred Networksが、研究、開発をしました。現在は、FaceBookのPythonフレームワークである、Pytorchに吸収されました。 0.4.0 Pythonのインストール Python のインストール 今回は、Pythonは省略します。 Pythonは、3.6以降、3.8までであれば正常に動作します。 Anacondaでも大丈夫です。 0.4.1 その他モジュール pip install -U pip setuptools pip install -U jupyterlab jupyter jupyter-console jupytext spyder matplotlib numpy 0.4.2 Cupyのインストール Chainerで、GPUを使用するためには、Cupyをインストールする必要があります。 pip install cupy-cuda101 確認: エラーが出なければ大丈夫です。 エラーが出た場合、CuPyのインストールがうまくできていないです。東京大学 金子研究室 に詳しく書いてあるので、エラーが出て進めない場合は参考にしてください。 0.4.3 Chainerのインストール pip install chainer==7.7 0.4.4 Chainerの動作確認 ChainerのGithub から、サンプルを落としてきます。 cd chainer-7.7.0 python examples\mniost\train_mnist.py -g 0 オプションは、gpuの番号です。-1で、gpuを使わずに計算できます。 0.5 学習データ 東京大学内のサーバー、将棋コンピュータ対局場(Flood gate)のデータを使います。 後のニューラルネットワークを学習させるための棋譜を用意します。 今回は、『Flood gate』から最新の2020年の全棋譜データをcsa形式で落とします。 http://wdoor.c.u-tokyo.ac.jp/shogi/x/wdoor2017.7z 上は、2017年の棋譜データですが、(webサイト更新されていない),2017→2020にすれば落とせます。(書いていいのか…?) http://wdoor.c.u-tokyo.ac.jp/shogi/x/wdoor2020.7z 7zipで解凍してください。 全棋譜: 13万件くらい(現状なので、今も増えています) そのあと、学習データとして適切でないデータもたくさんあるので、手数が50手以上で、レーティングが3000以上のものに限定して、学習データとします。ここら辺は、どんな方法でもいいです。 §1. ニューラルネットワーク ここからは、思考部分を実装します。 具体的に、局面から指し手を予測するようにします。 コード構成 \<policynetwork>(root dir) | setup.py | train_policy.py | kifulist_train.txt | kifulist_test.txt | kifulist_train_1000.txt | kifulist_test_100.txt |- <model> | | model_policy | |- <pydlshogi> | | common.py | | features.py | | read_kifu_.py | | | |- <network> | | policy.py | |- <utils> | | fileter_csa.py | | make_kifu_list.py | | plot_log.py 1.1 モジュールインストール import setuptoolssetuptools.setup( name = 'python-dlshogi', version = '0.0.1', author = 'SudaDaisuke', # 名前 packages = ['pydlshogi'], scripts = [],) スクリプトを別のスクリプトから、importできるように登録します。 プロジェクトのルートディレクトリで下のコマンドを打ちます。 pip install --no-cache-dir -e . 1.2 Policy Network 将棋の指し手を予測するための、ニューラルネットワークを構成します。 Alpha Go では、打ち手を探索する「Policy Network」と局面を評価する「Value Network」という2つの深層ニューラルネットワークで構成されています。 将棋の指し手を予測するために、 Alpha Go で採用された方法を改良して、13層の畳み込みニューラルネットワークを構成します。 ニューラルネットワークの仕様 項目 値 フィルターサイズ 3x3 中間層のフィルターサイズ 192 ストライド 1 パディング層 1 ブーリング層 1 活性化関数 ReLU 1.3 Policy Network 実装 上の設計をもとに実装していきます。 pydlshogi\network\policy.pyfrom chainer import Chainimport chainer.functions as Fimport chainer.links as Lfrom pydlshogi.common import *ch = 192class PolicyNetwork(Chain): def __init__(self): super(PolicyNetwork, self).__init__() with self.init_scope(): self.l1=L.Convolution2D(in_channels = 104, out_channels = ch, ksize = 3, pad = 1) self.l2=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l3=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l4=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l5=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l6=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l7=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l8=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l9=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l10=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l11=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l12=L.Convolution2D(in_channels = ch, out_channels = ch, ksize = 3, pad = 1) self.l13=L.Convolution2D(in_channels = ch, out_channels = MOVE_DIRECTION_LABEL_NUM, ksize = 1, nobias = True) self.l13_bias=L.Bias(shape=(9*9*MOVE_DIRECTION_LABEL_NUM)) def __call__(self, x): h1 = F.relu(self.l1(x)) h2 = F.relu(self.l2(h1)) h3 = F.relu(self.l3(h2)) h4 = F.relu(self.l4(h3)) h5 = F.relu(self.l5(h4)) h6 = F.relu(self.l6(h5)) h7 = F.relu(self.l7(h6)) h8 = F.relu(self.l8(h7)) h9 = F.relu(self.l9(h8)) h10 = F.relu(self.l10(h9)) h11 = F.relu(self.l11(h10)) h12 = F.relu(self.l12(h11)) h13 = self.l13(h12) return self.l13_bias(F.reshape(h13, (-1, 9*9*MOVE_DIRECTION_LABEL_NUM))) 1.2 学習処理 1.2.1 実装 train_policy.pyimport numpy as npimport chainerfrom chainer import cuda, Variablefrom chainer import optimizers, serializersimport chainer.functions as Ffrom pydlshogi.common import *from pydlshogi.network.policy import PolicyNetworkfrom pydlshogi.features import *from pydlshogi.read_kifu import *import argparseimport randomimport pickleimport osimport reimport loggingparser = argparse.ArgumentParser()parser.add_argument('kifulist_train', type=str, help='train kifu list')parser.add_argument('kifulist_test', type=str, help='test kifu list')parser.add_argument('--batchsize', '-b', type=int, default=32, help='Number of positions in each mini-batch')parser.add_argument('--test_batchsize', type=int, default=512, help='Number of positions in each test mini-batch')parser.add_argument('--epoch', '-e', type=int, default=1, help='Number of epoch times')parser.add_argument('--model', type=str, default='model/model_policy', help='model file name')parser.add_argument('--state', type=str, default='model/state_policy', help='state file name')parser.add_argument('--initmodel', '-m', default='', help='Initialize the model from given file')parser.add_argument('--resume', '-r', default='', help='Resume the optimization from snapshot')parser.add_argument('--log', default=None, help='log file path')parser.add_argument('--lr', type=float, default=0.01, help='learning rate')parser.add_argument('--eval_interval', '-i', type=int, default=1000, help='eval interval')args = parser.parse_args()logging.basicConfig(format='%(asctime)s\t%(levelname)s\t%(message)s', datefmt='%Y/%m/%d %H:%M:%S', filename=args.log, level=logging.DEBUG)model = PolicyNetwork()model.to_gpu()optimizer = optimizers.SGD(lr=args.lr)optimizer.setup(model)# Init/Resumeif args.initmodel: logging.info('Load model from {}'.format(args.initmodel)) serializers.load_npz(args.initmodel, model)if args.resume: logging.info('Load optimizer state from {}'.format(args.resume)) serializers.load_npz(args.resume, optimizer)logging.info('read kifu start')# 保存済みのpickleファイルがある場合、pickleファイルを読み込む# train datetrain_pickle_filename = re.sub(r'\..*?$', '', args.kifulist_train) + '.pickle'if os.path.exists(train_pickle_filename): with open(train_pickle_filename, 'rb') as f: positions_train = pickle.load(f) logging.info('load train pickle')else: positions_train = read_kifu(args.kifulist_train)# test datatest_pickle_filename = re.sub(r'\..*?$', '', args.kifulist_test) + '.pickle'if os.path.exists(test_pickle_filename): with open(test_pickle_filename, 'rb') as f: positions_test = pickle.load(f) logging.info('load test pickle')else: positions_test = read_kifu(args.kifulist_test)# 保存済みのpickleがない場合、pickleファイルを保存するif not os.path.exists(train_pickle_filename): with open(train_pickle_filename, 'wb') as f: pickle.dump(positions_train, f, pickle.HIGHEST_PROTOCOL) logging.info('save train pickle')if not os.path.exists(test_pickle_filename): with open(test_pickle_filename, 'wb') as f: pickle.dump(positions_test, f, pickle.HIGHEST_PROTOCOL) logging.info('save test pickle')logging.info('read kifu end')logging.info('train position num = {}'.format(len(positions_train)))logging.info('test position num = {}'.format(len(positions_test)))# mini batchdef mini_batch(positions, i, batchsize): mini_batch_data = [] mini_batch_move = [] for b in range(batchsize): features, move, win = make_features(positions[i + b]) mini_batch_data.append(features) mini_batch_move.append(move) return (Variable(cuda.to_gpu(np.array(mini_batch_data, dtype=np.float32))), Variable(cuda.to_gpu(np.array(mini_batch_move, dtype=np.int32))))def mini_batch_for_test(positions, batchsize): mini_batch_data = [] mini_batch_move = [] for b in range(batchsize): features, move, win = make_features(random.choice(positions)) mini_batch_data.append(features) mini_batch_move.append(move) return (Variable(cuda.to_gpu(np.array(mini_batch_data, dtype=np.float32))), Variable(cuda.to_gpu(np.array(mini_batch_move, dtype=np.int32))))# trainlogging.info('start training')itr = 0sum_loss = 0for e in range(args.epoch): positions_train_shuffled = random.sample(positions_train, len(positions_train)) itr_epoch = 0 sum_loss_epoch = 0 for i in range(0, len(positions_train_shuffled) - args.batchsize, args.batchsize): x, t = mini_batch(positions_train_shuffled, i, args.batchsize) y = model(x) model.cleargrads() loss = F.softmax_cross_entropy(y, t) loss.backward() optimizer.update() itr += 1 sum_loss += loss.data itr_epoch += 1 sum_loss_epoch += loss.data # print train loss and test accuracy if optimizer.t % args.eval_interval == 0: x, t = mini_batch_for_test(positions_test, args.test_batchsize) y = model(x) logging.info('epoch = {}, iteration = {}, loss = {}, accuracy = {}'.format(optimizer.epoch + 1, optimizer.t, sum_loss / itr, F.accuracy(y, t).data)) itr = 0 sum_loss = 0 # validate test data logging.info('validate test data') itr_test = 0 sum_test_accuracy = 0 for i in range(0, len(positions_test) - args.batchsize, args.batchsize): x, t = mini_batch(positions_test, i, args.batchsize) y = model(x) itr_test += 1 sum_test_accuracy += F.accuracy(y, t).data logging.info('epoch = {}, iteration = {}, train loss avr = {}, test accuracy = {}'.format(optimizer.epoch + 1, optimizer.t, sum_loss_epoch / itr_epoch, sum_test_accuracy / itr_test)) optimizer.new_epoch()logging.info('save the model')serializers.save_npz(args.model, model)logging.info('save the optimizer')serializers.save_npz(args.state, optimizer) 上のコードは、学習部分を実装しています。 具体的に、 こんな感じに実装されています。 1.2.2 学習実行 実際に学習を実行すると、こんな感じで、損失計算と、学習データから得られた結果との精度です。 §2. 将棋AI実装 ニューラルネットワークで、学習を終えたモデルを使って対局できるように、USIエンジンにします。 USI(Universal Shogi Interface)プロトコルとは、将棋GUIソフトと思考エンジンが通信をするために、Tord Romstad氏によって考案された通信プロトコルです。 http://shogidokoro.starfree.jp/usi.html 上のように、USIプロトコルをもとに通信することで、将棋AIをGUI上で動かします。 フォルダ構成 \<policynetwork>(root dir) |- <bat> | | Docbase.bat | |- <pydlshogi> | |- <player> | | base_player.py | | Docbase_player.py | | | |- <usi> | | usi.py | | usi_Docbase_player.py import numpy as npimport chainerfrom chainer import serializersfrom chainer import cuda, Variableimport chainer.functions as Fimport shogifrom pydlshogi.common import *from pydlshogi.features import *from pydlshogi.network.policy import *from pydlshogi.player.base_player import *def greedy(logits): return logits.index(max(logits))def boltzmann(logits, temperature): logits /= temperature logits -= logits.max() probabilities = np.exp(logits) probabilities /= probabilities.sum() return np.random.choice(len(logits), p=probabilities)class PolicyPlayer(BasePlayer): def __init__(self): super().__init__() self.modelfile = r'学習したモデルのパス' self.model = None def usi(self): print('id name DocBase ShogiAI') print('option name modelfile type string default ' + self.modelfile) print('usiok') def setoption(self, option): if option[1] == 'modelfile': self.modelfile = option[3] def isready(self): if self.model is None: self.model = PolicyNetwork() self.model.to_gpu() serializers.load_npz(self.modelfile, self.model) print('readyok') def go(self): if self.board.is_game_over(): print('bestmove resign') return features = make_input_features_from_board(self.board) x = Variable(cuda.to_gpu(np.array([features], dtype=np.float32))) with chainer.no_backprop_mode(): y = self.model(x) logits = cuda.to_cpu(y.data)[0] probabilities = cuda.to_cpu(F.softmax(y).data)[0] # 全ての合法手について legal_moves = [] legal_logits = [] for move in self.board.legal_moves: # ラベルに変換 label = make_output_label(move, self.board.turn) # 合法手とその指し手の確率(logits)を格納 legal_moves.append(move) legal_logits.append(logits[label]) # 確率を表示 print('info string {:5} : {:.5f}'.format(move.usi(), probabilities[label])) # 確率が最大の手を選ぶ(グリーディー戦略) selected_index = greedy(legal_logits) # 確率に応じて手を選ぶ(ソフトマックス戦略) selected_index = boltzmann(np.array(legal_logits, dtype=np.float32), 0.5) bestmove = legal_moves[selected_index] print('bestmove', bestmove.usi()) 上のように、AIの情報や局面ごとの最善手を計算していきます。 §3. GUIソフトで動かす 上で作ったモデルと実際に対局します。 GUIソフトはいくつかありますが、 将棋所 を使用します。 上のように、登録することができます。 §4. 参考 参考 URL 将棋AIで学ぶディープラーニング https://book.mynavi.jp/ec/products/detail/id=88752 Alpha Goの論文 https://www.nature.com/articles/nature16961 §5. 終わりに いかがでしょうか? 私は、ボードゲームが趣味で将棋、囲碁、チェス等ももちろん好きです。 チェスは、1997年に、当時のチャンピオン Гaрри Каспaров さんが、IBM社のディープブルーに敗れました。私がまだ生まれていない時代から、AI vs 人間の戦いが始まっていたことを知ったときは、驚愕しました。そして、あと10年は人間に勝てないといわれていた、囲碁も2016年に李 世乭 さんが、Google傘下のDemis Hassabis さんが率いるDeepMind社のAlpha Goに敗れました。 今回の将棋AIも、Alpha Goを参考にしています。 そして、将棋も2010年代からずっと、ponanzaといわれる将棋ソフトの強さは注目されていました。ponanzaには、モンテカルロ法が採用されていました。当時から、将棋プロでさえ、ponanzaを一目おいていて、当時の竜王である渡辺明さんとponanzaの公開対局が催されたりしました。(渡辺明竜王(当時)の逆転勝ち) 2016年、 Alpha Goの論文 が公開されると、すぐにAlpha Zeroと呼ばれる将棋AIが誕生しました。現在の、将棋界は藤井聡太2冠をはじめとして、若年世代を筆頭に、世代を問わず将棋AIを用いた研究が盛んになっています。 コンピュータ将棋大会も、開かれていて、将棋AIの強さを競う大会もあります。ぜひ、今回の記事に興味をもった方はBERTで、将棋AIを作ってみてください! https://www.youtube.com/watch?v=2Vl6Ao4GaSQ&t=853s

動画

該当するコンテンツが見つかりませんでした

書籍