20211013のPythonに関する記事は26件です。

【Django】ManyToManyFieldをQオブジェクトで検索する時の注意点

目的 多対多の関係にある動画モデルとタグモデルを用意した 動画のタイトルと動画に紐づいているタグを複数指定して、動画を検索する機能を追加する モデルのイメージ from django.db import models class Movie(models.Model): """動画""" title = models.CharField() tag = models.ManyToManyField(Tags) class Tag(models.Model): """タグ""" name = models.CharField() 動画テーブル(movie) ID タイトル 1 A動画 2 B動画 中間テーブル(movie_tag) ID 動画ID タグID 1 1 1 2 1 2 3 1 3 4 2 2 5 2 4 タグテーブル(tags) ID タグ名 1 ホラー 2 恋愛 3 SF 4 歴史 失敗したコード ネットで調査しつつ以下の実装を行った 失敗コード class MovieSearch(generics.ListAPIView): def get(self, request): # キーワード抽出 params = request.GET.get('keyword') # 全角スペースを半角スペースに変換 # 半角スペースで分割 keywords = keyword_serializer.validated_data.get('keyword').replace(' ', ' ').split(' ') # Qオブジェクトを生成 query = Q() # タイトルとタグをキーワードで絞り込む for keyword in keywords: query &= (Q(title__icontains=keyword) | Q(tag__name__icontains=keyword)) # 対象の動画一覧取得 queryset = Movie.object.filter(query) キーワードにホラーと恋愛を指定して、動画Aが取得できることを期待した しかし、結果は0件で何も取得できなかった 原因調査 filterの後ろにqueryとつけることで、生クエリを確認することができる print(Movie.object.filter(query).query)をコードに定義し、生クエリを確認した SELECT /*長いので省略*/ FROM movie LEFT OUTER JOIN movie_tag ON (movie.uuid = movie_tag.movie_id) LEFT OUTER JOIN tags ON (movie_tag.tags_id = tags.id) WHERE (movie.title LIKE %ホラー% OR tags.name LIKE %ホラー%) AND (movie.title LIKE %恋愛% OR tags.name LIKE %恋愛%)) 中間テーブルとタグテーブルをLEFT OUTER JOINしており、結合されたテーブルを(タイトル | タグ)のAND条件で検索していた 結合テーブルのイメージ 動画ID タイトル 中間テーブルID タグID タグ名 1 A動画 1 1 ホラー 1 A動画 2 2 恋愛 1 A動画 3 3 SF 2 B動画 4 2 恋愛 2 B動画 5 4 歴史 ホラーと恋愛をもつ1レコードなど存在しないため、結果が0件になっていた 修正したコード 自力で色々試して、とりあえずやりたいことは以下のコードで実現できた 成功コード class MovieSearch(generics.ListAPIView): def get(self, request): # キーワード抽出 params = request.GET.get('keyword') # 全角スペースを半角スペースに変換 # 半角スペースで分割 keywords = keyword_serializer.validated_data.get('keyword').replace(' ', ' ').split(' ') # 対象の動画一覧取得 queryset = Movie.object.all() # タイトルとタグをキーワードで絞り込む for keyword in keywords: queryset = queryset.filter(Q(title__icontains=keyword) | Q(tag__name__icontains=keyword)) # 重複レコードを削除 queryset = queryset.distinct() ホラーと恋愛で検索すると、1回目は結合テーブルをホラーで、2回目は1回目の検索結果を元に結合テーブルを作成しホラーと恋愛で検索するため、重複レコードが発生する そのため、distinct()で重複レコードを作成する処理を追加した 懸念 毎回filterを実行するので、処理時間は遅いでしょう。 大人しく生クエリ書くか・・・・ もし、もっと良い方法があればどなたか教えていただければと思います。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【深層学習】活性化関数Leaky ReLUについて

深層学習モデルを構築する時、うまく活性化関数を選ぶことは大事です。 前編に続き、もう一つ活性化関数Leaky ReLUについて紹介したいと思います。 Leaky ReLUとは? Leaky ReLUはReLUの派生形の一つです。 数式を書くと f(x) = max(ax, x) ちなみに、$a$の数値は0.01で設定される場合が多いです。 数式により、$x$が負数の場合であれば$f(x)$は$ax$になるでしょう。 Pythonで書いてみると def leaky_relu(x): return max(0.01*x, x) Leaky ReLUの微分 $x$が正数の場合であれば: \frac{\mathrm{d} f(x)}{\mathrm{d} x}=\frac{\mathrm{d} x}{\mathrm{d} x}=1 $x$が$0$以下の場合であれば: \frac{\mathrm{d} f(x)}{\mathrm{d} x}=\frac{\mathrm{d} ax}{\mathrm{d} x}=a Pythonのif文で書いてみると def leaky_relu_derivative(x): if x >= 0 : return 1 if x < 0 : return 0.01 Leaky ReLUのグラフを作成 import numpy as np import matplotlib.pyplot as plt # inputデータ作成 x = np.linspace(-10, 10, 100) plt.figure(figsize=(8,6)) plt.plot(x, list(map(lambda x: leaky_relu(x), x)), label="Leaky ReLU") plt.plot(x, list(map(lambda x: leaky_relu_derivative(x), x)), label="Derivative of Leaky ReLU") plt.title("Leaky ReLU") plt.legend() plt.show() オレンジの線を見ると、 $x\ge0$の時、微分の数値は$1$で、 $x\lt 0$の時、微分の数値は$0$に近いですが、実は$0.01$です。 以上、簡単にメモしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでひらがなフォルダ名を生成するやり方

起こったこと python2.7でフォルダ名の文字列が文字化けする ちなみに1行目に# -- coding: utf-8 -- はつけてます。 ex.py name = "あいうえお" os.makedirs(name) これを実行するとフォルダ名が文字化けする windowsのフォルダ名の文字コード てっきりshift-jisかと思っていたけどやってみるとutf-8っぽかった コマンドプロンプト内ではshift-jisらしいがフォルダ名やファイル名はutf-8らしい。 ちなみにpython内で文字列の扱いは難しい https://qiita.com/yubessy/items/9e13af05a295bbb59c25 https://qiita.com/miyase256/items/0e8cc40d95bf6236729e が内容が分かりやすかった。 というわけで https://qiita.com/inoory/items/aafe79384dbfcc0802cf を参考にutf8に変換した ex2.py name = "あいうえお" utf_name = name.decode("utf-8", errors="surrogateescape") os.makedirs(utf_name) encodeで文字列をバイト列 decodeでバイト列を文字列に直しているっぽい 内部的にはバイト列だったのか・・・? とりあえずこれで日本語表示できるようになったのでよしとする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

モジュールがないなら(半)自動でインストールすればいいじゃない

Qiitaは今まで見る専だったのですが、情報整理やスキルアップも兼ねて恐る恐る記事を書いてみました。 環境 Windows 10 Python2.7 及び 3.7 PyCharm 2020 端的にまとめると 長くなってしまったためやったことを端的にまとめると 1. 『pkgutil.iter_modules()』を使用して全モジュールの一覧を取得 2. インストールしたいモジュールの一覧から全モジュール一覧にないモジュール名を抽出 3. 『subprocess.call()』でインストールしたいpipに抽出したモジュール一覧を文字列にして渡す 以上になります。 こんなことが以前ありました Python2と3の両方で動く処理を検証する度に別々の仮想環境をPyCharmで作成していてモジュールをインストールしてる時にふと思いました。 「これ、違う環境で度々インストールしなくちゃいけないのどうにかならないかな」 と。 インストールする作業もモジュールを一つ追加するだけなら問題無いとは思います。ですがそれ以上になるともうやってられなくて・・・。 そして思ったわけです。 「自分で作ってみるか・・・?」 と。 必要になりそうな要素を挙げていく インストールするモジュールの一覧 これに関しましては自分で用意すれば良いですね インストールされているモジュールの一覧 既にインストールされているモジュールのことを考えるとこれをどうするかが鍵 インストールされているモジュールの一覧を取得してみる 公式ドキュメントを探した結果『pkgutil.iter_modules』を使うことで読み込めるモジュールの情報を取得出来そうなのでそこから一覧を抽出してみます。 import pkgutil pkg_list = [pkg for pkg in pkgutil.iter_modules()] 取得したリストの中を見てみると1番目にモジュール名っぽいのが格納されているのを確認出来ました。ということなので以下のようにしてみました。 import pkgutil pkg_list = [pkg[1] for pkg in pkgutil.iter_modules()] インストールしたいモジュールの一覧として抽出してみる では次にインストール処理を無駄に走らせない為にインストールされているモジュールを除いたリストを作成してみます。 important = [] # インポートしたいモジュール名のリスト import_list = [i for i in important if i not in pkg_list] これでリストが出来ました。それでは作成したリストからpipに渡す引数の文字列を作成してインストールができるか走ってもらいます。 pipでモジュールをインストール subprocessモジュールを使ってpipでインストールする際の記述をします。 import sys import subprocess if len(import_list) > 0: subprocess.call("{} -m pip install {}".format(sys.executable.replace("\\", "/"), " ".join(import_list))) こんな感じになりました。今実行しているPythonの環境に紐づいているpipにインストールしたいのでsys.executable関数を使用してPythonの実行ファイルパスを取得してます。 結果 処理を走らせた後にpipのモジュール一覧を見てみるとモジュールをきちんとインストールできてることが確認できました。 おまけ 試しにこれらの処理の後にpipにインストールしたモジュールをインポートする記述をした上で処理を走らせてみましたが問題なく動く模様。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AnacondaでJupyter Notebookにnbextensionsをインストールする

はじめに Jupyter Notebookは、そのままでは、タイプしても候補が出ない(自動補完しない)為、エディターとしては使いにくい印象があります。 ここでは、Jupyter Notebookに自動補完(入力候補)機能を追加する為に、Anacondaを前提として、よく使われるnbextensionsをインストールする方法を説明します。 Anacondaの場合、「『pipは使うな』と書いてある、でも、『pipでインストールする記事しかない』」、ことが多く、未知のパッケージはインストールが怖い、というのが正直な所ではないでしょうか。 正確を期すため、英語のnbextenstionsの公式サイトを見ながら、実際にやってみました。 当該ページの日本語訳も、筆者が行いましたので、参考までに載せておきます。 【nbextenstions(英語)】 https://jupyter-contrib-nbextensions.readthedocs.io/en/latest/install.html 前提条件 Anacondaがインストールされている AnacondaからJupyter Notebookをインストールしている 尚、筆者の場合、バージョンはPython3.7.6 / Anaconda 4.10.1 / jupyter-notebook 6.3.0 で、Windows10です。 バージョンはアナコンダ・プロンプトで それぞれ python --version、conda -V、jupyter-notebook --version とタイプして確認できます。 内容 1. Anacondaは原則condaを使う。pipは可能な限り使わない。 2. nbextensionsのインストール方法 2.1 conda-forgeから jupyter_contrib_nbextensions をインストール 2.2 Javascriptとcssのインストール 2.3 nbextensionsの有効化 3. nbextensionsの公式ページの抄訳 4. 参考ページ 1. Anacondaは原則condaを使う。pipは可能な限り使わない。 パッケージをインストールする場合、よく見るコマンドが pip install です。しかし、よく言われるように、Anaconda環境では、可能な限りpip installは避けるべきです。 詳しくは、以下のリンクを参照してください。(※Python公式サイトではありません) https://www.python.jp/install/anaconda/pip_and_conda.html ですから、Anaconda環境では、pip ではなく、conda コマンドを使うようにしてください。 【注意: リンク先運営者のコメント】 Pythonの公式サイトではなく、Pythonを開発する Python Software Foundation とも無関係です。 (中略) 当サイトは、もともと Pythonドキュメント翻訳プロジェクト が翻訳した日本語版Pythonドキュメントを公開するために運用していましたが、翻訳ドキュメントが python.org 本家で公開されることになり、現在は公開しておりません。 2. nbextensionsのインストール方法 この先、Anaconda環境(つまり、コマンドはcondaでインストールする。pipは使わない。)点に注意してください。 2.1 conda-forgeからjupyter_contrib_nbextensionsをインストール Anaconda Prompt(アナコンダ・プロンプト)で以下を入力し、Enter。 conda install -c conda-forge jupyter_contrib_nbextensions 最後に[y/n]ときいてくるので、yを入力しEnter。完了すると、doneと表示される。 ("conda-forge"は、condaのdefaultsにはない、パッケージが集積されたコミュニティです。) 注意: DOSのコマンドプロンプトではありません。Anacondaをインストールすると、DOSと似たようなアイコンのAnaconda Promptがインストールされます。Anaconda Promptを起動すると、(base)と各行のはじめに表示されるはずです。(base)と出ていなければ、DOSを開けているのではないでしょうか? jupyter-nbextensions-configuratorのインストールは必要か? 自分でインストールする必要ありません。これは、後で日本語訳の中にあるように、jupyter-contrib-nbextensionsをインストールすれば、自動でインストールされます。仮に、インストールをconda installで試みても、「既にインストール済み」と返してくるだけです。 2.2 Javascriptとcssのインストール 同じく、Anaconda Prompt(アナコンダ・プロンプト)で以下を入力し、Enter。 jupyter contrib nbextension install --user 2.3 nbextensionsの有効化 Jupyter Notebookを起動すると、新しくNbextensionsというタブが表れます。 左上のチェックボックスのチェックを外します。 (全てのチェックボックスがグレーアウトされているのは、これは、今の時点では、全てが無効になっているからです。) 尚、チェックボックスの英語は、以下のように言っています。 英語:disable configuration for nbextensions without explicit compatibility (they may break your notebook environment, but can be useful to show for nbextension development) 邦訳:明確な互換性のないnbexntensionsの(各機能の)設定を無効にする。(notebookの環境を破壊するかもしれないが、nbextensionの効果を示すには有効である。) 筆者の解釈: 海外サイトも調べましたが、免責と思われます。「nbextensionsの機能を同時に使う(複数チェックする)と、機能がうまく動かないかもれないし、それはnotebookの環境をおかしくするかもしれない。けれど、nbextensionsの便利な機能を利用できる。」ということだと思います。 しかし、チェックを外さない限り、機能を利用できません。よくあるパターンですよね。 左上の"dsiable..."のチェックを外したら、Hinterlandにチェックを入れます。 これで、新しいnotebookを作成すれば、候補が出るようになります。 3. nbextensionsの公式ページの抄訳 以下、英語のnbextensionsの公式ページの参考日本語訳です。一部省略しています。[*]マークのある項目は全て以下のnbextensionsのサイトの日本語訳です。 注意:公益性が高いと判断し、筆者が勝手に翻訳したもので、公式なものではありません。自動翻訳も一切使用していません。 [*] jupyter_contrib_nbextensionsをインストールする jupyter_contrib_nbextensionsをインストールするには、3つのステップが必要です。まず、Python pipパッケージのインストールが必要です。次に、notebook extensionsをJupyterのデータ・ディレクトリにコピーします。最後に、notebook extensionsを有効にします。有効化は、Jupyterのコマンドを使うか、より便利な方法としてjupyter_nbextensions_configuratorを使う方法があります。 [*] 1. pyhonパッケージをインストールする PIP nbextensionsはpyhonパッケージとして提供されます。インストールは、pipまたはsetup.pyスクリプトを実行する、通常の方法で行います。PyPiからインストールする場合は、シンプルに pip install jupyter_contrib_nbextensions とタイプします。あるいは、マスターブランチから直接インストールすることもできます。 pip install https://github.com/ipython-contrib/jupyter_contrib_nbextensions/tarball/master インストールには、--upgrade flagや -eなどのpipの通常のオプションが指定可能です。 Conda "notebook extensions"と"jupyter_nbextensions_configurator"のcondaパッケージは、conda-forgeから利用可能です。以下で、両方をインストールできます※2。 conda install -c conda-forge jupyter_contrib_nbextensions これを実行すると、JavascriptとCSSファイルが自動でインストールされるので(jupyter contrib nbextension install --sys-prefixを使います)、以下のインストール手順の第2ステップは省略できます。 ※2 : (筆者追記) "jupyter_contrib_nbextensions"をすれば、"jupyter_nbextensions_configurator"が自動でインストールされるので、後者を自らインストールする必要はありません。 [*]リポジトリをCloneしてインストール リポジトリからcloneしてインストールすることも可能で、開発には有効な方法です。以下でcloneする事が可能で、 git clone https://github.com/ipython-contrib/jupyter_contrib_nbextensions.git 次に、pipを実行します。 pip install -e jupyter_contrib_nbextensions [*] 2. javascriptとcssファイルをインストールする このステップで、nbextensionsのjavascriptとcssファイルをjupyterのディレクトリにコピーし、jupyterのconfigファイルを編集します。なお、jupyterのサブコマンドが用意されています。 jupyter contrib nbextension install --user コマンドは次の2つを実行します。nbextensionファイルをインストールし、nbconvertのconfigファイルを編集します。最初の部分は、本質的にはnotebookが提供するjupyter nbextension installのラッパーであり、関連するjavascriptとcssファイルを適切なjupyterのディレクトリにコピーします。2番目の部分は、configファイルである、jupyter_nbconvert_config.jsonと jupyter_notebook_config.jsonを以下に述べるオプションで編集します。コマンドの大部分は、jupyterが提供するものと全く同じコマンドです。オプションには以下を含みます。 --user ユーザーのjupyterのホームディレクトリにインストールします。 (他のオプションは省略) uninstallコマンドも用意され、全てのnbextensionのファイルをjupyterのディレクトリから削除します。 [*] 3. 機能の有効化、無効化 nbextensionを使う為には、更に、機能を有効化する必要があります。これは、notebookインターフェースがロードできるようにするということです。その為に、Jupyterのサブコマンドが使用可能です。 jupyter nbextension enable <nbextension require path> 例えば、 jupyter nbextension enable codefolding/main 無効化するには、以下を使います。 jupyter nbextension disable <nbextension require path> 他の方法として、そしてより便利な方法として、"jupyter_nbextensions_configurator"を使う事ができます。これは、このリポジトリ(筆者追記:jupyter_contrib_nbextensionsのこと)に付属してインストールされます。これにより、(筆者追記:コマンドで)オプションで設定するのと同じように、個々のnbextensionsの機能を有効化、無効化できます。nbextensionsタブがnotebookのページに表示され、そこからチェックボックスにより有効化、無効化が可能となります。さらに、それぞれの機能についての短い説明が表示され、設定のオプションも表示されます。 [*] 4. 更に複雑なセットアップ 殆どのnbextensionsの機能はjupyterhubで動くはずです。しかし、jupyterlabでは動きません。複雑あるはカスタマイズされたインストールをする場合は、Jupyter homepageにあるドキュメントを参照してください。 installing Jupyter もご参照ください。 4. 参考ページ 実践で使うPythonの技術が掲載されている弊社のページです。書籍のように構成されています。 https://shilabo.com/python/web_self/ pipとcondaの関係: https://www.python.jp/install/anaconda/pip_and_conda.html nbextensionsのインストール : https://www.soudegesu.com/post/python/jupyter-autocomplete/ https://qiita.com/simonritchie/items/d7dccb798f0b9c8b1ec5 https://qiita.com/SaitoTsutomu/items/1326e05eb992a8aa849d
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ColabでOpenCV GPUを使っていく~~

概要 ColabでOpencv GPUを使用する それだけです。 はじめに Colabは無料で使えて、なおかつGPUまで無料で使えるのがいいですよね。 ある時、「OpencvのGPUバージョンも、もしかしたらColabで使えのでは?」と思い、方法を探したときの備忘録になります。 ColabでOpencv GPU pipでインストールできるopencv-pythonやopencv-contrib-pythonでは、GPUは使えないですよね(もし、使えるのであれば僕の苦労が水の泡となってしまう) なので、1からbuildする必要があります。 また、僕が実験した感じ、opencvのバージョンは最新じゃないとopencvが認識されませんでした。 参考にしたサイトは以下になります。 ColabのGPUでOpenCVを使用する方法は? コード まずは、opencvとopencv_contribをクローンして来て、buildします。参考サイトのコードそのまま持ってきたので、DNNの部分とかは無くていいですね。 !git clone https://github.com/opencv/opencv !git clone https://github.com/opencv/opencv_contrib !mkdir /content/build %cd /content/build !cmake -DOPENCV_EXTRA_MODULES_PATH=/content/opencv_contrib/modules -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_EXAMPLES=OFF -DWITH_OPENEXR=OFF -DWITH_CUDA=ON -DWITH_CUBLAS=ON -DWITH_CUDNN=ON -DOPENCV_DNN_CUDA=ON /content/opencv !make -j8 install 割と時間がかかるので、終わったら以下のコマンドで確認 import cv2 print(cv2.cuda.getCudaEnabledDeviceCount()) 1 みたいに使えるデバイスの数が出てきたらOK 一応動作確認として動かしてみます。 import cv2 from google.colab.patches import cv2_imshow img_src = cv2.imread("images.png") img_gpu_src = cv2.cuda_GpuMat() img_gpu_src.upload(img_src) img_gpu_dst = cv2.cuda.resize(img_gpu_src, (300, 300)) img_dst = img_gpu_dst.download() cv2.imwrite("Ξガンダム.png", img_dst) 元画像 294×172 変換後 300×300 画像引用 : 目指したのは“脱ガンダム”『閃光のハサウェイ』Ξガンダムデザインの裏側 無事変換できました。 クシィガンダム...カッコよすぎますね。次の第二部が楽しみです。 おわりに 今回はColabでOpencv GPUを使う方法を紹介させて頂きました。正直いつ使うかわからないですが、色々調べてやっとこさ出来たので、とりあえず執筆しました。誰かのお役に立てれば幸いです。 閃光のハサウェイが小説と同じ展開かそうでないか、気になりすぎて夜も眠れない...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クラス自体に対しての型ヒントに使える python の typing.Type

python の typing モジュールの Type についてご存知でしょうか。これは、クラス自体に対しての型ヒントに使えるものです。 ところで、私が初めてこれについて調べようと思った際、 "python typing.Type" といったキーワードで検索してみました。しかし、その際に検索結果として主に表示されたのは python の型ヒント全般に関する記事でした。そのため、所望の情報にたどりつい着くまでに少々苦労しました。そこで python の公式ドキュメントの typing のページを見直したところ、 こちら に書いてました。(やはり最初に公式ドキュメントをしっかり読むべきですね。) 今回、 typing.Type について簡単にまとめたく、本記事を作成しました。 typing.Type とは 下記、 日本語版公式ドキュメント からの引用です。 Type[C] と注釈が付けられた変数は、そのクラス自身を受理します つまり、 Type は、自作クラス自体や、 int などの組み込みの型(クラス)自体を指し示すために使われます。 使用例 全く実用性のない例になりますが、簡単のため、下記のような例を作成しました。 type.py from typing import Type class Person: @staticmethod def get_cls_name() -> str: return "Person" def get_cls_name(cls: Type[Person]) -> str: return cls.get_cls_name() Person クラスにクラス名を返す staticmethod を定義し、その外に、get_cls_name 関数を定義しています。 ※もちろん、クラス名は cls.__name__ で取得すれば良いです。また、 Person.get_cls_name はリテラル値を返すので、アノテーションを Literal["Person"] とした方が良いです。 get_cls_name の型ヒント get_cls_name(cls: Type[Person]) -> str は、下記を意味します。 get_cls_name 関数の引数 cls は( Person のインスタンスではなく) Person 自身であり、返り値の型は str である。 そのため、 get_cls_name 関数を呼び出す際は、 get_cls_name(Person) のように呼び出します。 TypeVar との併用 TypeVar 自身については、 こちら に簡単な記事を作成しましたので、適宜ご参照ください。 TypeVar と併用する場合の使用例を下記のように作成しました。 typevar_and_type.py from typing import Type, TypeVar Number = TypeVar("Number", int, float) def to_number(str_number: str, type_: Type[Number]) -> Number: return type_(str_number) to_number の型ヒント to_number(str_number: str, type_: Type[Number]) -> Number は、下記を意味します。 to_number 関数の引数 str_number の型は str、 type_ は int または float 自身であり、 type_ が int 自身の際の返り値の型は int、 type_ が float 自身の際の返り値の型は float である。 そのため、 to_number 関数を呼び出す際は、 to_number("10", int) のように呼び出します。 まとめ typing.Type は、クラス自体を示す型ヒントとして用いられる。 以上です。閲覧ありがとうございました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python3環境で日本語を使うとUnicodeEncodeErrorが出る問題

この記事は以前に投稿したものですが、諸事情でアカウントを作り直したため改めて投稿しています。内容は少し古いかもしれません。 背景 私の環境でpython(3.6.2)の文字列に日本語を使うとUnicodeEncodeErrorが出てしまいました。すぐ直せるだろうと軽く考えていたのですが、案外解決に手間取ったので備忘録がてら記事にしておこうと思いました。python2系で同様の問題と解決策を記載したページはいくつかヒットしたのですが、python3系の情報は私の検索の仕方が悪かったのか、まだ情報が少ないようです。 (ないとは言っていません。すでにもっと良質の情報をwebに公開されている先達の方はどうかご容赦ください。) 動作に失敗した環境 MacOS High Sierra上のdockerコンテナ dockerイメージはconda/miniconda3をpullし、conda update —allしたもの python version 3.6.2 サンプルコード jp.py print('日本語をプリントします') # python ./jp.py Traceback (most recent call last):   File "jp.py", line 1, in <module>     print('\u65e5\u672c\u8a9e\u3092\u30d7\u30ea\u30f3\u30c8\u3057\u307e\u3059') UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-10: ordinal not in range(128) 最終解決策 まず、locale -aコマンドを実行します。利用可能なロケール一覧が表示されます。 私のdockerコンテナでは以下のようになりました。 # locale -a C C.UTF-8 POSIX 次に、環境変数LC_CTYPEにlocale -aで表示された利用可能ロケールのうち、UTF-8を含むものをセットします。私の環境では # export LC_CTYPE="C.UTF-8" または、環境変数LANGにlocale -aで表示された利用可能ロケールのうち、UTF-8を含むものの前半(.の前の部分)をセットします。 私の環境では # export LANG="C.UTF-8" これで日本語が表示されるようになりました。 # python ./jp.py 日本語をプリントします 以上です。locale -aでUTF-8を含むロケールがなかった場合、あるいはUNIX系OSでない場合の解決方法はわかりません。現状、そのような状況でお困りの方のお役には立てなさそうです。 試してみてダメだったこと ソースコードのエンコーディングを指定する jp_1.py # -*- coding: utf-8 -*- print('日本語をプリントします') 同じエラーが出現しました。 ソースコードの文字列をunicodeオブジェクトと明示的に表示する jp_2.py # -*- coding: utf-8 -*- print(u'日本語をプリントします') 同じエラーがでます。そもそも、Unicodeが表示できない、というエラーなので当たり前ですね。 ソースコードの文字列をバイト列として扱う jp_3.py # -*- coding: utf-8 -*- print(b'日本語をプリントします') 今度はSyntaxError: bytes can only contain ASCII literal characters.という別のエラーに変わります。 sys.stdoutに文字コードを指定する jp_4.py # -*- coding: utf-8 -*- import sys import codecs sys.stdout = codecs.getwriter('utf-8')(sys.stdout) print('日本語をプリントします') 実行結果 Traceback (most recent call last):   File "./jp_4.py", line 7, in <module>     print('\u65e5\u672c\u8a9e\u3092\u30d7\u30ea\u30f3\u30c8\u3057\u307e\u3059')   File "/usr/local/lib/python3.6/codecs.py", line 377, in write     self.stream.write(data) TypeError: write() argument must be str, not bytes Python2系では解決策の一つとして記載のあったやり方ですが、今回は上手くいきませんでした。Python3系ではcodecsの扱い方が変わったのでしょうか? 詳しくは追求していませんが、いずれにしてもすべてのソースコードにこのような付加物を付けるのはあまり現実的ではないと思いました。 環境変数LANGをja_JPまたはja_JP.UTF-8に設定する # export LANG="ja_JP" #または"ja_JP.UTF-8" # python ./jp_1.py Traceback (most recent call last):   File "./jp_1.py", line 2, in <module>     print('\u65e5\u672c\u8a9e\u3092\u30d7\u30ea\u30f3\u30c8\u3057\u307e\u3059') UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-10: ordinal not in range(128) 日本語を表示させたいのだからja_JPにすればよいのだろうと安易に考えましたが、ダメでした。最終的に上手くいった方法から考察するに、利用可能ロケールにja_JPがなかったからと思われます。もちろん、locale -aでja_JP.UTF-8が表示されるような環境ならこれでも上手くいくと思います。 簡単な考察 まず、Pythonインタプリタがなぜ日本語を正しく表示できないのかを考えてみます。エラーメッセージを読むと、'ascii codec can't encode …と表示されているので、超意訳すると「Pythonはこういう文字コードも扱えるけど使ってる端末がasciiしか扱えないから表示できないよ」とお節介を焼いていると読めます。ではPythonはどういう手段で端末の扱える文字コードのcodecを認識しているのでしょうか? これはPython標準ライブラリドキュメントのsys.getfilesystemencoding()の項に記載されています。このドキュメントによると、  Mac OS X では、エンコーディングは 'utf-8' となります。 On Unix, the encoding is the locale encoding.  On Windows, the encoding may be 'utf-8' or 'mbcs', depending on user configuration. との記載があります。従って、MacやWindowsではデフォルト設定でも大丈夫と思われますがそのほかのUNIX系OS(Linux含む)ではlocaleの設定に依存するということが分かります。よって、localeの環境変数が正しく設定されていなかったり、空白だったりするとデフォルトの'ascii'が使用されてしまい、日本語が正しく表示されなくなるということのようです。LC_CTYPEを参照しているところまではドキュメントには記載されていませんでしたので、Python自体の実装を見てみないとそこまでは分からないのかもしれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SPSS Modelerの2値分類モデル評価のための精度分析ノードと評価フラグノードをPythonで書き換える。信用リスクの判定モデルの評価

SPSS Modelerの精度分析ノードと評価グラフノードをPythonで書き換えます。 0.データ まず以下のようなデータを用いて決定木モデルを作ります。 目的変数 Risk:信用リスク 説明変数 Age:年齢 Income:収入ランク Credit_cards:クレジットカード枚数 Education:学歴 Car_loans:車のローン数 年齢や収入ランクから信用リスクを判定する2値分類のモデルを評価します。 1m.①説明変数、目的変数の定義とモデルの作成 Modeler版 まず、データ型ノードで説明変数、目的変数の定義を行う必要があります。 Riskの尺度を「フラグ型」に変更し、「値の読み込み」ボタンをクリックします。その後、CustIDのロールを「なし」、Riskのロールを「対象」(目的変数の意味)を設定します。Age、Income、Credit_cards、Education、Car_loansは「入力」(説明変数の意味)のままにします。 C&R Treeのモデル作成ノードを接続します。そのまま実行してもモデルは作成できるのですが、ここではDecisionTreeClassifierとなるべく似た設定にします。 まず、「作成オプション」タブ「基本」項目の「過剰適合を回避するためにツリーを剪定」のチェックを外します。 もうひとつ、詳細設定項目の「オーバーフィット防止設定」を0にし、実行します。 1p.①説明変数、目的変数の定義とモデルの作成 Sckit-Learn版 scikit-learnの場合、説明変数、目的変数の定義はXとyの変数に入力します。 #説明変数と目的変数の定義 X = df[['Age', 'Car_loans', 'Credit_cards', 'Education','Income']] y =df['Risk'] Sckit-LearnのCARTモデルはDecisionTreeClassifierで作ります。 max_depthなどのパラメーターはModelerのデフォルト値に近いもので指定しました。 fit(X, y)で説明変数と目的変数を与えてモデルを作成します。 モデル作成 #Cartモデル作成のパッケージ from sklearn.tree import DecisionTreeClassifier #モデル作成 clf = DecisionTreeClassifier(max_depth=5,min_samples_split=0.02,min_samples_leaf=0.01,min_impurity_decrease=0.0001) clf = clf.fit(X, y) 2m.②精度評価、混同行列、AUC Modeler版 作成されたモデルナゲットに「精度分析ノード」をつなぎます。 様々な評価指標がありますが、ここではよく使う一致行列と評価メトリックを選択し、実行します。一致行列は混同行列といった方がなじみがあるかもしれません。 正解にある%が精度(Accuracy)です。 一致行列が混同行列です。 評価メトリックのAUCがAUCです。 これらの評価指標を一度に計算できます。 2p.②精度評価、混同行列、AUC Sckit-Learn版 sckitlearnで精度を計算するのはaccuracy_scoreです。 引数には正解データのyと予測結果clf.predict(X)を与えます。 accuracy_score from sklearn.metrics import accuracy_score y_pred=clf.predict(X) print('accuracy_score:%f' % accuracy_score(y, y_pred)) accuracy_score:0.818994 混同行列はconfusion_matrixで計算します。 引数はaccuracy_scoreと同じ正解データと予測結果です。 confusion_matrix from sklearn.metrics import confusion_matrix cm = confusion_matrix(y, y_pred) print(cm) [[1729 135] [ 311 289]] AUCはroc_auc_scoreで計算します。 引数には正解データのyとTrueの確信度であるclf.predict_proba(X)[:,1]を与えます。 roc_auc_score from sklearn.metrics import roc_auc_score y_score=clf.predict_proba(X)[:,1] print('roc_auc_score:%f' % roc_auc_score(y, y_score)) roc_auc_score:0.862200 3m.③ROC曲線、ゲイングラフ Modeler版 ROCの値だけでなく、ROC曲線やゲイングラフで評価をします。 「評価」のグラフノードをモデルナゲットに接続します。 グラフの種類をROCにし、実行して表示します。 次にグラフの種類をゲインにして表示します。 3p.③ROC曲線、ゲイングラフ Sckit-Learn版 ROC曲線はroc_curveでFalse Positive RateとTrue Positive Rateを取得してプロットします。 引数には正解データのyとTrueの確信度を与えます。 roc_curve from sklearn.metrics import roc_curve from sklearn.metrics import auc import matplotlib.pyplot as plt fpr, tpr, thresholds = roc_curve(y, y_score) auc = auc(fpr, tpr) # ROC曲線をプロット plt.plot(fpr, tpr, label='ROC curve (area = %.2f)'%auc) plt.legend() plt.title('ROC curve') plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.grid(True) 以下の記事を参考にしました:ROC曲線とAUCの出力 - Qiita ゲインのグラフについてはscikitplotのplot_cumulative_gainを使います。 引数には正解データのyとTrueとFalseの確信度を与えます。 plot_cumulative_gain import scikitplot as skplt skplt.metrics.plot_cumulative_gain(y, clf.predict_proba(X)[:]) クラスは0と1の両方が表示されます。 4m.④Precision,Recall,F1スコア Modeler版 Modelerの「精度分析ノード」でAccuracyや混同行列はでるのですが、Precision,Recall,F1スコアを出したいこともよくあります。 以下の記事のように算出をすることができます。 Modelerデータ加工Tips#04-行列入替で適合率PrecisionやF1スコア・MCCを求める | IBM ソリューション ブログ 上のブログとちょっと違うのですが、ここでは以下のようなノードの組合せで混同行列のTP,TN,FP,FNを計算しています。 算出したTP,TN,FP,FNからPrecision,Recall,F1スコアを算出します。 4p.④Precision,Recall,F1スコア Sckit-Learn版 Accuracyと同様にPrecision,Recall,F1スコアは算出できます。 Precision,Recall,F1スコア from sklearn.metrics import precision_score,recall_score,f1_score print('precision_score:%f' % precision_score(y, y_pred)) print('recall_score:%f' % recall_score(y, y_pred)) print('f1_score:%f' % f1_score(y, y_pred)) precision_score:0.681604 recall_score:0.481667 f1_score:0.564453 いっぺんにレポートをするclassification_reportという命令もあります。 classification_report from sklearn.metrics import classification_report print(classification_report(y, y_pred)) precision recall f1-score support 0 0.85 0.93 0.89 1864 1 0.68 0.48 0.56 600 accuracy 0.82 2464 macro avg 0.76 0.70 0.73 2464 weighted avg 0.81 0.82 0.81 2464 5. サンプル サンプルは以下に置きました。 ストリーム https://github.com/hkwd/200611Modeler2Python/blob/master/AnalysisEvaluation/analysisEvaluation.str?raw=true notebook https://github.com/hkwd/200611Modeler2Python/blob/master/AnalysisEvaluation/analysisevaluation.ipynb データ https://raw.githubusercontent.com/hkwd/200611Modeler2Python/master/data/credit_risk.csv ■テスト環境 Modeler 18.3 Windows 10 64bit Python 3.8.10 pandas 1.0.5 sklearn 0.23.2 scikitplot 0.3.7 6. 参考情報 精度分析ノード - IBM Documentation モデル評価の結果の読み込み - IBM Documentation sklearn-metrics-metrics API Reference — scikit-learn 1.0 documentation scikit-learnで混同行列を生成、適合率・再現率・F1値などを算出 | note.nkmk.me
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SPSS Modelerの2値分類モデル評価のための精度分析ノードと評価グラフノードをPythonで書き換える。信用リスクの判定モデルの評価

SPSS Modelerの精度分析ノードと評価グラフノードをPythonで書き換えます。 0.データ まず以下のようなデータを用いて決定木モデルを作ります。 目的変数 Risk:信用リスク 説明変数 Age:年齢 Income:収入ランク Credit_cards:クレジットカード枚数 Education:学歴 Car_loans:車のローン数 年齢や収入ランクから信用リスクを判定する2値分類のモデルを評価します。 1m.①説明変数、目的変数の定義とモデルの作成 Modeler版 まず、データ型ノードで説明変数、目的変数の定義を行う必要があります。 Riskの尺度を「フラグ型」に変更し、「値の読み込み」ボタンをクリックします。その後、CustIDのロールを「なし」、Riskのロールを「対象」(目的変数の意味)を設定します。Age、Income、Credit_cards、Education、Car_loansは「入力」(説明変数の意味)のままにします。 C&R Treeのモデル作成ノードを接続します。そのまま実行してもモデルは作成できるのですが、ここではDecisionTreeClassifierとなるべく似た設定にします。 まず、「作成オプション」タブ「基本」項目の「過剰適合を回避するためにツリーを剪定」のチェックを外します。 もうひとつ、詳細設定項目の「オーバーフィット防止設定」を0にし、実行します。 1p.①説明変数、目的変数の定義とモデルの作成 Sckit-Learn版 scikit-learnの場合、説明変数、目的変数の定義はXとyの変数に入力します。 #説明変数と目的変数の定義 X = df[['Age', 'Car_loans', 'Credit_cards', 'Education','Income']] y =df['Risk'] Sckit-LearnのCARTモデルはDecisionTreeClassifierで作ります。 max_depthなどのパラメーターはModelerのデフォルト値に近いもので指定しました。 fit(X, y)で説明変数と目的変数を与えてモデルを作成します。 モデル作成 #Cartモデル作成のパッケージ from sklearn.tree import DecisionTreeClassifier #モデル作成 clf = DecisionTreeClassifier(max_depth=5,min_samples_split=0.02,min_samples_leaf=0.01,min_impurity_decrease=0.0001) clf = clf.fit(X, y) 2m.②精度評価、混同行列、AUC Modeler版 作成されたモデルナゲットに「精度分析ノード」をつなぎます。 様々な評価指標がありますが、ここではよく使う一致行列と評価メトリックを選択し、実行します。一致行列は混同行列といった方がなじみがあるかもしれません。 正解にある%が精度(Accuracy)です。 一致行列が混同行列です。 評価メトリックのAUCがAUCです。 これらの評価指標を一度に計算できます。 2p.②精度評価、混同行列、AUC Sckit-Learn版 sckitlearnで精度を計算するのはaccuracy_scoreです。 引数には正解データのyと予測結果clf.predict(X)を与えます。 accuracy_score from sklearn.metrics import accuracy_score y_pred=clf.predict(X) print('accuracy_score:%f' % accuracy_score(y, y_pred)) accuracy_score:0.818994 混同行列はconfusion_matrixで計算します。 引数はaccuracy_scoreと同じ正解データと予測結果です。 confusion_matrix from sklearn.metrics import confusion_matrix cm = confusion_matrix(y, y_pred) print(cm) [[1729 135] [ 311 289]] AUCはroc_auc_scoreで計算します。 引数には正解データのyとTrueの確信度であるclf.predict_proba(X)[:,1]を与えます。 roc_auc_score from sklearn.metrics import roc_auc_score y_score=clf.predict_proba(X)[:,1] print('roc_auc_score:%f' % roc_auc_score(y, y_score)) roc_auc_score:0.862200 3m.③ROC曲線、ゲイングラフ Modeler版 ROCの値だけでなく、ROC曲線やゲイングラフで評価をします。 「評価」のグラフノードをモデルナゲットに接続します。 グラフの種類をROCにし、実行して表示します。 次にグラフの種類をゲインにして表示します。 3p.③ROC曲線、ゲイングラフ Sckit-Learn版 ROC曲線はroc_curveでFalse Positive RateとTrue Positive Rateを取得してプロットします。 引数には正解データのyとTrueの確信度を与えます。 roc_curve from sklearn.metrics import roc_curve from sklearn.metrics import auc import matplotlib.pyplot as plt fpr, tpr, thresholds = roc_curve(y, y_score) auc = auc(fpr, tpr) # ROC曲線をプロット plt.plot(fpr, tpr, label='ROC curve (area = %.2f)'%auc) plt.legend() plt.title('ROC curve') plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.grid(True) 以下の記事を参考にしました:ROC曲線とAUCの出力 - Qiita ゲインのグラフについてはscikitplotのplot_cumulative_gainを使います。 引数には正解データのyとTrueとFalseの確信度を与えます。 plot_cumulative_gain import scikitplot as skplt skplt.metrics.plot_cumulative_gain(y, clf.predict_proba(X)[:]) クラスは0と1の両方が表示されます。 4m.④Precision,Recall,F1スコア Modeler版 Modelerの「精度分析ノード」でAccuracyや混同行列はでるのですが、Precision,Recall,F1スコアを出したいこともよくあります。 以下の記事のように算出をすることができます。 Modelerデータ加工Tips#04-行列入替で適合率PrecisionやF1スコア・MCCを求める | IBM ソリューション ブログ 上のブログとちょっと違うのですが、ここでは以下のようなノードの組合せで混同行列のTP,TN,FP,FNを計算しています。 算出したTP,TN,FP,FNからPrecision,Recall,F1スコアを算出します。 4p.④Precision,Recall,F1スコア Sckit-Learn版 Accuracyと同様にPrecision,Recall,F1スコアは算出できます。 Precision,Recall,F1スコア from sklearn.metrics import precision_score,recall_score,f1_score print('precision_score:%f' % precision_score(y, y_pred)) print('recall_score:%f' % recall_score(y, y_pred)) print('f1_score:%f' % f1_score(y, y_pred)) precision_score:0.681604 recall_score:0.481667 f1_score:0.564453 いっぺんにレポートをするclassification_reportという命令もあります。 classification_report from sklearn.metrics import classification_report print(classification_report(y, y_pred)) precision recall f1-score support 0 0.85 0.93 0.89 1864 1 0.68 0.48 0.56 600 accuracy 0.82 2464 macro avg 0.76 0.70 0.73 2464 weighted avg 0.81 0.82 0.81 2464 5. サンプル サンプルは以下に置きました。 ストリーム https://github.com/hkwd/200611Modeler2Python/blob/master/AnalysisEvaluation/analysisEvaluation.str?raw=true notebook https://github.com/hkwd/200611Modeler2Python/blob/master/AnalysisEvaluation/analysisevaluation.ipynb データ https://raw.githubusercontent.com/hkwd/200611Modeler2Python/master/data/credit_risk.csv ■テスト環境 Modeler 18.3 Windows 10 64bit Python 3.8.10 pandas 1.0.5 sklearn 0.23.2 scikitplot 0.3.7 6. 参考情報 精度分析ノード - IBM Documentation モデル評価の結果の読み込み - IBM Documentation sklearn-metrics-metrics API Reference — scikit-learn 1.0 documentation scikit-learnで混同行列を生成、適合率・再現率・F1値などを算出 | note.nkmk.me
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python mmap 使い方

本日はプロセス間、スレッド間などで値をやり取りする方法を紹介します。 C言語などを触ったことある人は聞いたことある人もいるかもしれません、 「共有メモリ (Shared Memory)」という仕組みです。 共有メモリとは ざっくりですが共有メモリとは、名前の通りPCのメモリ上の一部を複数のプロセス間で使用できるようにする仕組みです。 最初はこのくらいの理解で十分だと思います。 Python mmap公式ドキュメント ソースコード ClsMMap.py ''' --------------------------- 共有メモリ管理クラス ---------------------------''' import mmap import os WORD_SIZE = 2 '''1WORD = 2byte''' MAP_SIZE = 1024 * WORD_SIZE MAP_FILE_NAME = "./map.dat" class classMMap: def __init__(self) -> None: """コンストラクタ """ self._mm = None ''' map file check ''' if os.path.exists(MAP_FILE_NAME): ''' Read memory map ''' self._readMMapFile() else: ''' create and initialize ''' self._createMMapFile() ''' Read memory map ''' self._readMMapFile() print(" __init__ comp.") def _createMMapFile(self): with open(MAP_FILE_NAME, mode="wb") as file: initStr = '00' * MAP_SIZE initByte = bytes.fromhex(initStr) file.write(initByte) print("_createMMapFile fin.") def _readMMapFile(self): with open(MAP_FILE_NAME, mode="r+b") as file: self._mm = mmap.mmap(file.fileno(), 0) self._mm.seek(0) print("_readMMapFile fin.") def ReadShort(self, adr:int): """指定したアドレスの値を読み込む (読み込みデータ最大値 : 1WORD = 2bytes = 65535) Args: adr : 読み込みたいアドレス """ try: self._mm.seek(adr*WORD_SIZE) bytes = self._mm.read(WORD_SIZE) val = int.from_bytes(bytes, 'little',signed=True) self._mm.seek(0) return val except Exception as e: print("ReadShort except" + str(e).replace('\n','')) return def WriteShort(self, adr:int, data) -> None: """指定したアドレスに値を書き込む (書き込みデータ最大値 : 1WORD = 2bytes = 65535) Args: adr (int): 書き込みたいアドレス data (short): 書き込むデータ """ if data >= (2**(8*WORD_SIZE)): print("Error. Over 2bytes (65535)") return try: ddata = int(data) bytes = ddata.to_bytes(WORD_SIZE, 'little',signed=True) for i in range(WORD_SIZE): self._mm[adr*WORD_SIZE + i] = bytes[i] except Exception as e: print("WriteShort except" + str(e).replace('\n','')) def despose(self): self._mm.close() 共有メモリ使用例 これから実際に使用するサンプルも紹介します。(今回はスレッド間でのデータやり取りで例を示そうと思います。) app.py import time import threading from ClsMmap import classMMap ''' 上で作成した共有メモリ操作クラス ''' TEST_ADR = 100 def func1(): """100番アドレスを1秒ごとに読み出す """ while True: print(clsMMap.ReadShort(TEST_ADR)) time.sleep(1) def func2(): """100番アドレスを1秒ごとに書き換える(1 ~ 10000の範囲) """ while True: curVal = clsMMap.ReadShort(TEST_ADR) clsMMap.WriteShort(TEST_ADR, (curVal + 1) % 10000) ''' 値のインクリメント ''' time.sleep(1) if __name__ == '__main__': ''' 共有メモリ管理クラス インスタンス生成 ''' clsMMap = classMMap() ''' 読み込み用スレッド ''' thread1 = threading.Thread(target=func1) thread1.start() ''' 書き込み用スレッド ''' thread2 = threading.Thread(target=func2) thread2.start() ※実務で利用する場合は排他処理や例外処理をもう少し実装する必要があります。 mmapのClose()処理も適宜呼び出してください。 Close()をしない場合は、プログラム終了時、最後の状態を自動的に 共有メモリファイルに書き込んでくれます。 以上が今回の内容となります。是非参考にしてください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python mmap 使い方 (共有メモリ操作)

本日はプロセス間、スレッド間などで値をやり取りする方法を紹介します。 使用する手法は、C言語などを触ったことある人は聞いたことある人もいるかもしれませんが 「共有メモリ (Shared Memory)」という仕組みです。 Pythonで共有メモリを実装するのに便利な「mmap」というライブラリがあるので、 本日は紹介したいと思います。 共有メモリとは ざっくりですが共有メモリとは、名前の通りPCのメモリ上の一部を複数のプロセス間で使用できるようにする仕組みです。 最初はこのくらいの理解で十分だと思います。 複数のPythonアプリ間でデータの通信ができるので便利です。 応答速度もかなり高速です。 Python mmap公式ドキュメント ソースコード ClsMMap.py ''' --------------------------- 共有メモリ管理クラス ---------------------------''' import mmap import os WORD_SIZE = 2 '''1WORD = 2byte''' MAP_SIZE = 1024 * WORD_SIZE MAP_FILE_NAME = "./map.dat" class classMMap: def __init__(self) -> None: """コンストラクタ """ self._mm = None ''' map file check ''' if os.path.exists(MAP_FILE_NAME): ''' Read memory map ''' self._readMMapFile() else: ''' create and initialize ''' self._createMMapFile() ''' Read memory map ''' self._readMMapFile() print(" __init__ comp.") def _createMMapFile(self): with open(MAP_FILE_NAME, mode="wb") as file: initStr = '00' * MAP_SIZE initByte = bytes.fromhex(initStr) file.write(initByte) print("_createMMapFile fin.") def _readMMapFile(self): with open(MAP_FILE_NAME, mode="r+b") as file: self._mm = mmap.mmap(file.fileno(), 0) self._mm.seek(0) print("_readMMapFile fin.") def ReadShort(self, adr:int): """指定したアドレスの値を読み込む (読み込みデータ最大値 : 1WORD = 2bytes = 65535) Args: adr : 読み込みたいアドレス """ try: self._mm.seek(adr*WORD_SIZE) bytes = self._mm.read(WORD_SIZE) val = int.from_bytes(bytes, 'little',signed=True) self._mm.seek(0) return val except Exception as e: print("ReadShort except" + str(e).replace('\n','')) return def WriteShort(self, adr:int, data) -> None: """指定したアドレスに値を書き込む (書き込みデータ最大値 : 1WORD = 2bytes = 65535) Args: adr (int): 書き込みたいアドレス data (short): 書き込むデータ """ if data >= (2**(8*WORD_SIZE)): print("Error. Over 2bytes (65535)") return try: ddata = int(data) bytes = ddata.to_bytes(WORD_SIZE, 'little',signed=True) for i in range(WORD_SIZE): self._mm[adr*WORD_SIZE + i] = bytes[i] except Exception as e: print("WriteShort except" + str(e).replace('\n','')) def despose(self): self._mm.close() 共有メモリ使用例 これから実際に使用するサンプルも紹介します。(今回はスレッド間でのデータやり取りで例を示そうと思います。) app.py import time import threading from ClsMmap import classMMap ''' 上で作成した共有メモリ操作クラス ''' TEST_ADR = 100 def func1(): """100番アドレスを1秒ごとに読み出す """ while True: print(clsMMap.ReadShort(TEST_ADR)) time.sleep(1) def func2(): """100番アドレスを1秒ごとに書き換える(1 ~ 10000の範囲) """ while True: curVal = clsMMap.ReadShort(TEST_ADR) clsMMap.WriteShort(TEST_ADR, (curVal + 1) % 10000) ''' 値のインクリメント ''' time.sleep(1) if __name__ == '__main__': ''' 共有メモリ管理クラス インスタンス生成 ''' clsMMap = classMMap() ''' 読み込み用スレッド ''' thread1 = threading.Thread(target=func1) thread1.start() ''' 書き込み用スレッド ''' thread2 = threading.Thread(target=func2) thread2.start() ※実務で利用する場合は排他処理や例外処理をもう少し実装する必要があります。 mmapのClose()処理も適宜呼び出してください。 Close()をしない場合は、プログラム終了時、最後の状態を自動的に 共有メモリファイルに書き込んでくれます。 以上が今回の内容となります。是非参考にしてください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ESP32に書き込めない(Macでの環境, Big Sur)

MacでESP32に書き込めない。 ESP32をMacに接続して、Arduino IDEで書き込もうとしたら、 エラーが出て、その先、何もできない。 コンパイルエラーみたい。 エラーメッセージをgoogle で検索してみると、 Qiitaの記事が見つかる。 この記事に従って、作業すればOKでした。 あとは、私自身の作業の忘備録としての記録を残しておきます。 今回の作業内容 Macはpythonのデフォルトが、python2なので、  python3への切り替えが必要ということで 上のURLの作業を行えば、OKのはずが、 python3がインストールされていないことが判明。 よって、Python3をインストール。 というか、 https://www.python.org/  にアクセスして、 Downloadから、最新版(今回は、3.10.0)をダウンロードして、インストール。 ここまで、やっても、arduino IDEでのESP32のコンパイルエラーが消えない。 ターミナルのコマンドプロンプトで、 python3 を入力しても エラーがでて、python3を認識していないみたい。 (パスが通っていない) python3をインストールした後は、再起動したほうが、いいみたいです。 で、再起動。 再起動後に python3が 使えるようになりました。 $python3 --version   で、3.10.0が表示される。 (python だと 2系のバージョンが表示される。 これは、今のところそのままにしておいた。) Arduino IDE でコンパイルすると、メッセージが減ったけど、 serialがなんちゃらというエラーが出る。 これも、 先のQiitaの参考ページにある情報どおりで、 pip を使って、 pyserialをインストールすればよい。 ターミナルで、 pip install pyserial を 入力するが、 しかし、 pipが見つからないというコマンドが表示される。 次は、pipのインストール。 参考にしたのが、 こちらのQiitaの記事。   https://qiita.com/ohbashunsuke/items/e7c673db606a6dced8a6 もうね、本当ありがたい。  pipのインストールを済ませ、 pipを使って、 pyserial をインストールしたら、 Arduino IDEでESP32のコンパイルができるようになった。 でも、まだボードに書き込めない で、ボードに書き込むけど、エラーが表示されて、書き込めない。  ESP32の設定はOKのはず。  次のサイトと同じであることを確認済み。 https://www.indoorcorgielec.com/resources/arduinoide%E8%A8%AD%E5%AE%9A/esp-wroom-32%E6%90%AD%E8%BC%89%E8%A3%BD%E5%93%81/ 書き込みができない件を、ググると、 次のサイトがみつかった。 https://dotstud.io/docs/nefrybt-error-handling-2/ 書き込み速度を遅くするといいみたい。 460800bpsに設定したら、書き込みできた。 以上。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS:S3からのデータのダウンロードとアップロードについて

なぜ書こうと思ったのか インターン先で、AWSを初めて使うようになり、S3からのデータのダウンロードとアップロードが苦戦したので、整理のために書こうと思いました。 目次 そもそもS3とは? S3への接続とその確認方法 S3からのダウンロードとアップロード そもそもS3とは? Amazon Simple Storage Service、通称S3はオブジェクトストレージサービスであり、様々なユースケースのデータを保存して保護することができる。さらには、データを整理してアクセス制御を細かく調整するために使いやすい機能を提供するサービスである。 S3のメリットについては、以下の記事を参考 - S3のメリット S3への接続とその確認方法 今回はSagemaker上で行っています。 ローカルで行う場合は、configureの設定を行ってから進める必要があります。 バケットへの接続 name部分には、抽出したいファイルがあるS3上のバケット名をいれます。 import boto3 #バケットへの接続 s3 = boto3.resource('s3') bucket = s3.Bucket('name') このときに、jupyter上で print(bucket) を実行してバケット名が表示されていれば、接続ができていると思われます。 ファイルのダウンロードとアップロード バケットからのダウンロード 対象のファイルをダウンロードしたい場合 bucket.download_file(file_path, file_name) file_pathには、s3上での抽出したいファイルのpath file_nameには、保存時のファイル名 一度にすべてのファイルをダウンロードしたい場合 !aws s3 cp s3://'s3上でのバケット名'(/file_path) 'ダウンロード先のフォルダ' --exclude "" --include "*" --recursive excludeオプション exclude オプションは、コマンドからオブジェクトのみを除外するようにルールを設定します。 includeオプション include オプションは、指定したオブジェクトのみをコマンドに含めるようにルールを設定します。 recursiveオプション recursiveオプションは、指定のディレクトリ内またはプレフィックス内のすべてのファイルやオブジェクトに対してコマンドが実行されます バケットへのアップロード  bucket.upload_file(file_name, file_path) file_nameには、アップロードするファイル名 file_pathには、バケット内のどのフォルダ内にアップロードするかのpathを指定する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSから突然$52.92(6031円)の請求が来たときの話

はじめに 最近、pythonでAWSでのいくつかのAIサービスの学習をスタート AWSでAIサービスを使ってみる〜第11回Personalize編〜 ちょうどPersonalizeの試しの学習を終え、Qiitaへの記事投稿もひと段落した 約一週間後、、、、、、その通知は届き来ました。 ハイ!? $52.92? 一体これはどういうことなんだってばよ!? 気づいたときは 時すでに遅し 9月末からpersonalizeの請求が入っており、気づいたのが10月であったため10月分の請求も含まれていました。。。 Cost Explorer 何が起こっていたのか 実際の請求の内容はpersonalizeの学習の後、データセットを停止させずにそのままACTIVE状態のまま翌月まで起動させていたため(気づけよ)、無料枠の請求を超えて通常の請求が入ったようですね。 使用していなかったためpersonalizeのデータセットは削除しました。削除も順番があり、作成したキャンペーンやソリューションから削除する必要があるので注意です。 無料枠を超えたメールが来ていました。 終わりに AWSのAIサービスは将来的なWebサービスの中核として期待されていますが、ご利用には注意が必要ですね。 personalizeのみでなくForcastもいくらか請求額があるため、検証する事柄やデータは事前に調整して必要なデータのみ利用することをオススメします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

numpy.ndarrayとstd_msgs/MultiArrayの相互変換

はじめに ROSでpythonを使う際,numpy arrayをpublish/subscribeしたいときに変換する関数をまとめておきました. numpy ⇔ MultiArray変換 ポイントとしては,std_msgs/MultiArrayLayoutを変換したnumpy.ndarrayにあわせて変更する必要がある点です. converter.py #!/usr/bin/env python from functools import partial from std_msgs.msg import MultiArrayDimension, Float32MultiArray def _numpy2multiarray(multiarray_type, np_array): """Convert numpy.ndarray to multiarray""" multiarray = multiarray_type() multiarray.layout.dim = [MultiArrayDimension("dim%d" % i, np_array.shape[i], np_array.shape[i] * np_array.dtype.itemsize) for i in range(np_array.ndim)] multiarray_data = np_array.reshape(1, -1)[0].tolist() return multiarray def _multiarray2numpy(pytype, dtype, multiarray): """Convert multiarray to numpy.ndarray""" dims = map(lambda x: x.size, multiarray.layout.dim) return np.array(multiarray.data, dtype=pytype).reshape(dims).astype(dtype) numpy2f32multi = partial(_numpy2multiarray, Float32MultiArray) f32multi2numpy = partial(_multiarray2numpy, float, np.float32) partial()の部分をInt8MultiArray等で定義することで他のデータ型にも対応させることができます. Publish/Subscribe talker.py #!/usr/bin/env python import numpy as np import rospy from std_msgs.msg import Float32MultiArray from converter import numpy2f32multi def talker(): pub = rospy.Publisher('chatter', Float32MultiArray, queue_size=10) rospy.init_node('talker', anonymous=True) r = rospy.Rate(10) # 10hz while not rospy.is_shutdown(): arr = np.random.rand(2, 2, 2).astype(np.float32) arr_msg = numpy2f32multi(arr) pub.publish(arr_msg) r.sleep() if __name__ == '__main__': try: talker() except rospy.ROSInterruptException: pass listener.py #!/usr/bin/env python import numpy as np import rospy from std_msgs.msg import Float32MultiArray from converter import f32multi2numpy def callback(msg): arr = f32multi2numpy(msg) rospy.loginfo(arr) rospy.loginfo(type(arr)) rospy.loginfo(arr.shape) def listener(): rospy.init_node('listener', anonymous=True) rospy.Subscriber('chatter', Float32MultiArray, callback) rospy.spin() if __name__ == '__main__': listener()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Grundy数を使って1次元ドミノ置きゲームを解く(その2)

その1はこちら 【問題】1次元のマス目(1xN)に交互にドミノ(1x2)を置いて行って置けなくなった方が負けというゲームで、先手が必ず負けになるN<100をリストせよ。 この問題をGrundy数を使ってN=7までの結果の再び表にします。 N mex Grundy数 N=0 0 N=1 0 N=2 $mex(G(0))$ 1 N=3 $mex(G(1))$ 1 N=4 $mex(G(1),G(2))=mex(0,1)$ 2 N=5 $mex(G(3),G(1)\oplus G(2))=mex(1,1)$ 0 N=6 $mex(G(4),G(1)\oplus G(3),G(2)\oplus G(2))=mex(2,1,0)$ 3 N=7 $mex(G(5),G(1)\oplus G(4),G(2)\oplus G(3))=mex(0,2,0)$ 1 これから推測されるように、任意のNの時のGrundy数は以下の式で表せます。 $G(N) = mex(\\ G(0)\oplus G(N-2),\\ G(1)\oplus G(N-2-1),\\ ...\\ G(k)\oplus G(N-2-k))$ ここで$0\leqq k \leqq floor(N/2)$ これをそのままPythonのプログラムにします。まずmex関数を定義します。単純に0からリストの最大値まで見ていって、ない数があったらそれを返し、もし全部あった場合は最大値+1を返します。 def mex(gl): for i in range(max(gl)+2): if i not in gl: return i return ("error") これを使って$G(N)$の計算を行います。2つ前までのGrundy数のリストを逆順にしてXORを取り、mexを見つけてGrundy数のリストに加えるのを繰り返せばOKです。 (XORを取るのは半分までで良いのですがコードを簡単にするために全部取っています) import numpy as np n = 100 GL = np.array([0, 0]) # G(0), G(1) for i in range(2, n+1): GL = np.append(GL, mex(GL[:-1]^GL[-2::-1])) print(list(*np.where(GL==0))) # [0, 1, 5, 9, 15, 21, 25, 29, 35, 39, 43, 55, 59, 63, 73, 77, 89, 93, 97] これでオンライン整数列大辞典に掲載されていた数列のN<100が計算できました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

並列プログラミングライブラリParslの紹介

Parsl について Parsl の名前の由来は Parallel Scripting Library で、文字通りPython で実装された並列処理を行うためのライブラリです。 Parsl を使用すると、Pythonの関数とシェルやコマンドなどの外部コンポーネントで構成される並列プログラムを依存関係で結びつけて非同期・並列のワークフローを作成し実行することができます。ラップトップから共有メモリのマルチコアサーバー、小規模なHPCクラスター、クラウドのKubernetes、スーパーコンピュータまで、あらゆる計算リソースでParslプログラムを実行できるため、容易にスケールアップすることができます。 Parsl は次のような特徴を持っています。 コードと実行環境の分離:任意の実行プロバイダ(PC、クラスター、スーパーコンピュータ、クラウドなど)や実行モデル(スレッド、パイロットジョブなど)、エグゼキューター(Slurm、SGE、PBSなど)をサポートするように設計されています 暗黙的な並列処理:関数をデコレータでアノテーションするだけで非同期に実行される ファイル抽象化:ローカルファイルと同様にリモートファイルを扱うことができる データフローモデルのワークフローを定義することができる 実行中にワークフローが決定される動的ワークフローを定義できる ノートブックやその他のインタラクティブなメカニズムによるインタラクティブな並列プログラミング 実行状況のモニタリング こちらで、Jupyterlab でのオンラインデモ を試用することができます。 導入事例 で紹介されているように、Parslは様々な科学分野の幅広い研究者に利用されています。 インストール parsl は pip コマンドでインストールすることができます。 $ pip install parsl parsl の使用方法 次の例は、"hello world" を出力するPython の関数とBashスクリプトを使った簡単なParslプログラムの書き方です。 In [2]: # %load 01_intro.py ...: import parsl ...: import os ...: from parsl.app.app import python_app, bash_app ...: ...: conf = parsl.load() ...: ...: @python_app ...: def hello_python (message): ...: return f'Hello {message}' ...: ...: @bash_app ...: def hello_bash(message, stdout='hello-stdout'): ...: return f'echo "Hello {message}"' ...: ...: # Pythonアプリを起動 ...: p = hello_python('World (Python)') ...: ...: # Bashスクリプトを実行 ...: b = hello_bash('World (Bash)') ...: c = b.result() ...: ...: # 結果を表示する ...: print(p.result()) ...: with open('hello-stdout', 'r') as f: ...: print(f.read()) ...: ...: print(f'bash exit code: {c}') ...: Hello World (Python) Hello World (Bash) bash exit code: 0 In [3]: Parsl は Python の関数を @python_app デコレータを使ってアプリ(Apps)として、また @bash_app デコレータを使って外部アプリケーションを呼び出すアプリとしてラップします。デコレーションされた関数は、すべての入力の準備ができたときに並列に実行することができます。 実行ログ parsl プログラムを実行すると、カレントディレクトリに runinfoディレクトリが作成されて、実行ログが記録されます。 % tree -L 2 runinfo runinfo ├── 000 │   └── parsl.log └── 001 └── parsl.log 2 directories, 2 files 詳細はここでは触れませんが、次のようなログが記録されます。 % cat runinfo/000/parsl.log 2021-10-10 08:27:54.939 parsl.dataflow.dflow:84 [DEBUG] Starting DataFlowKernel with config Config( app_cache=True, checkpoint_files=None, checkpoint_mode=None, checkpoint_period=None, executors=[ThreadPoolExecutor( label='threads', managed=True, max_threads=2, storage_access=None, thread_name_prefix='', working_dir=None )], garbage_collect=True, initialize_logging=True, internal_tasks_max_threads=10, max_idletime=120.0, monitoring=None, retries=0, run_dir='runinfo', strategy='simple', usage_tracking=False ) 2021-10-10 08:27:54.939 parsl.dataflow.dflow:86 [INFO] Parsl version: 1.1.0 2021-10-10 08:27:54.939 parsl.dataflow.usage_tracking.usage:125 [DEBUG] Tracking status: False 2021-10-10 08:27:54.939 parsl.dataflow.dflow:114 [INFO] Run id is: 4cf091d5-7477-4531-8313-388558e2bd46 2021-10-10 08:27:55.149 parsl.dataflow.memoization:164 [INFO] App caching initialized 2021-10-10 08:27:55.149 parsl.dataflow.strategy:127 [DEBUG] Scaling strategy: simple 2021-10-10 08:27:55.163 parsl.dataflow.dflow:795 [DEBUG] Task 0 will be sent to executor threads (以下略) Parsl の set_stream_logger() を呼び出すと、実行ログは標準出力にも送出されるようになります。 In [2]: # %load 02_logging.py ...: import parsl ...: import os ...: from parsl.app.app import python_app, bash_app ...: ...: # すべてのログを標準出力へ送出 ...: parsl.set_stream_logger() ...: ...: conf = parsl.load() ...: ...: @python_app ...: def hello_python (message): ...: return f'Hello {message}' ...: ...: @bash_app ...: def hello_bash(message, stdout='hello-stdout'): ...: return f'echo "Hello {message}"' ...: ...: # Pythonアプリを起動 ...: p = hello_python('World (Python)') ...: ...: # Bashスクリプトを実行 ...: b = hello_bash('World (Bash)') ...: c = b.result() ...: ...: # 結果を表示する ...: print(p.result()) ...: with open('hello-stdout', 'r') as f: ...: print(f.read()) ...: ...: print(f'bash exit code: {c}') ...: 2021-10-10 08:29:17 parsl.dataflow.rundirs:35 [DEBUG] Parsl run initializing in rundir: runinfo/007 2021-10-10 08:29:17 parsl.dataflow.dflow:84 [DEBUG] Starting DataFlowKernel with config Config( app_cache=True, checkpoint_files=None, checkpoint_mode=None, checkpoint_period=None, executors=[ThreadPoolExecutor( label='threads', managed=True, max_threads=2, storage_access=None, thread_name_prefix='', working_dir=None )], garbage_collect=True, initialize_logging=True, internal_tasks_max_threads=10, max_idletime=120.0, monitoring=None, retries=0, run_dir='runinfo', strategy='simple', usage_tracking=False ) 2021-10-10 08:29:17 parsl.dataflow.dflow:86 [INFO] Parsl version: 1.1.0 2021-10-10 08:29:17 parsl.dataflow.usage_tracking.usage:125 [DEBUG] Tracking status: False 2021-10-10 08:29:17 parsl.dataflow.dflow:114 [INFO] Run id is: e0e4cbeb-4677-4643-b012-9bdd0427a036 (中略) Hello World (Python) Hello World (Bash) bash exit code: 0 In [3]: Parslの設定 Parslはコードと実行を分離します。そのためには、実行時に使用するリソースのプール(クラスター、クラウド、スレッドなど)を記述する構成モデルに依存します。 前述の例では、ローカルでの並列実行を容易にするために、スレッドのローカルプールを使用するように設定します。 conf = parsl.load() これは、次と同じことです。 from parsl.configs.local_threads import config conf = pars.load(config) これにより、デフォルトの設定が読み込まれます。 In [3]: conf.config Out[3]: Config( app_cache=True, checkpoint_files=None, checkpoint_mode=None, checkpoint_period=None, executors=[ThreadPoolExecutor( label='threads', managed=True, max_threads=2, storage_access=None, thread_name_prefix='', working_dir=None )], garbage_collect=True, initialize_logging=True, internal_tasks_max_threads=10, max_idletime=120.0, monitoring=None, retries=0, run_dir='runinfo', strategy='simple', usage_tracking=False ) 使用状況の追跡 Parsl はオープンソースで無料で使用することができます。しかし、Parsl プロジェクトは米国の資金提供機関 NSF(National Science Foundatioon) から資金提供を受けて開発がされています。Parsl プロジェクトが資金提供を受け続けるため、およびNSF自身が資金提供を主張するためには、こうした投資によって科学界が恩恵を受けていることを証明する必要があることから、Parsl では使用状況を追跡するための機能を備えています。 環境変数 PARSL_TRACKING=trueを設定するか、設定オブジェクト(parsl.config.Config)でusage_tracking=Trueを設定することで、Parslプロジェクトへ使用状況を送信することができます。 デフォルトは通知しないようになっています。 Parslプロジェクトでは、可能な限り匿名化された使用状況の追跡から、使用状況の測定、バグの特定、ユーザビリティ、信頼性、パフォーマンスの向上を図っています。利用状況の集計は、NSFへの報告目的でのみ使用されます。 Parslプロジェクトに送信される情報は次のものです。 匿名化されたユーザーID 匿名化されたホスト名 匿名化されたParslスクリプトID 開始時刻と終了時刻 Parslの終了コード 使用されたエクゼキュータの数 失敗の数 ParslとPythonのバージョン OSとOSのバージョン この機能の詳細は、 ドキュメントの Usage statistics collectionを参照してください。 Parsl の構成要素 Parsl は次のコンポーネントから構成されています。 アプリ(Apps):python_app、bash_app、join_app Futures:AppFutures、DataFutures File:ローカルファイル、リモートファイル ワークフロー アプリ(Apps) Parslでのアプリ(Apps)とは、実行リソース(クラウド、クラスタ、ローカルPCなど)上で非同期に実行できるコードの断片のことをいいます。Parslは純粋なPythonアプリ(python_app)と、Bashで実行されるコマンドラインアプリ(bash_app)、他のアプリを組み合わせてサブワークフローを形成することができるJoinアプリ(join_app)を提供しています。 Pythonアプリ(python_app) 最初の例として、文字列'Hello World!'を返す簡単なPython関数を定義してみましょう。この関数は、@python_appデコレーターを使ってParslアプリにします。 @python_app def hello (): return 'Hello World!' print(hello().result()) In [2]: # %load 03_python_app.py ...: import parsl ...: from parsl.app.app import python_app ...: # from parsl.configs.local_threads import config ...: ...: config = parsl.load() ...: ...: @python_app ...: def hello_python(): ...: return 'Hello world' ...: ...: future = hello_python() ...: print(future .result()) ...: Hello world In [3]: この例からもわかるように、python_app は標準的なPythonの関数呼び出しをラップしています。そのため、任意の引数を渡すことができ、標準的なPythonオブジェクトを返すことができます。 In [2]: # %load 04_multiply.py ...: import parsl ...: from parsl.app.app import python_app ...: # from parsl.configs.local_threads import config ...: ...: config = parsl.load() ...: ...: @python_app ...: def multiply(a, b): ...: return a * b ...: ...: app = multiply(5, 9) ...: print(app.result()) ...: 45 In [3]: Parslアプリはリモートで実行される可能性があることに注意してください。そのため、必要な依存関係はすべて関数本体に含めておく必要があります。例えば、アプリがdatetimeライブラリを必要とする場合、関数内でそのライブラリをインポートする必要があります。 In [2]: # %load 05_hello_with_time.py ...: import parsl ...: from parsl.app.app import python_app ...: # from parsl.configs.local_threads import config ...: ...: config = parsl.load() ...: ...: @python_app ...: def hello_python(): ...: import datetime ...: return f'Hello world: {datetime.datetime.now()}' ...: ...: app = hello_python() ...: print(app.result()) ...: Hello world: 2021-10-10 09:08:14.044477 Bashアプリ(bash_app) ParslのBashアプリは、Bashシェルのように、外部アプリケーションの実行をコマンドラインからラップすることができます。また、Bashスクリプトを直接実行するためにも使用することができます。Bashアプリを定義するには、ラップされたPython関数が、実行されるコマンドライン文字列を返す必要があります。 Bashアプリの最初の例として、Linuxのコマンドechoを使って文字列Hello World!を返してみましょう。この関数は@bash_appデコレーターを使ってBashアプリにしています。 なお、echoコマンドは、Hello World! を標準出力に出力します。この出力を使用するためには、標準出力をキャプチャするようにParslに指示する必要があります。これは app 関数で stdout キーワード引数を指定することで行います。stderrをキャプチャするときも同様です。 In [2]: # %load 06_bash_app.py ...: import parsl ...: from parsl.app.app import bash_app ...: # from parsl.configs.local_threads import config ...: ...: config = parsl.load() ...: ...: @bash_app ...: def echo_hello(stdout='echo-hello.stdout', stderr='echo-hello.stderr'): ...: return 'echo "Hello World!"' ...: ...: app = echo_hello() ...: c = app.result() ...: ...: with open('echo-hello.stdout', 'r') as f: ...: print(f.read()) ...: ...: print(f'ExitCode: {c}') ...: Hello World! ExitCode: 0 In [3]: result()`メソッドを呼び出すと、実行したコマンドの終了コードが返されます。 Joinアプリ Joinアプリは @join_app デコレーターで指定できます。 Joinアプリは、ワークフローがいくつかのアプリを起動する必要があるが、いくつかの初期のアプリが完了するまで、それらのアプリを特定できない場合に便利です。 例えば、前処理ステージの後にN個の中間ステージが続くが、Nの値は前処理が完了するまでわからない場合や、実行するアプリの選択が前処理の出力に依存する場合などです。 次のコードは、pre_process()の後に、option_one()またはoption_two()のアプリを選択し、その後にpost_process()を呼び出す場合の例です。 @python_app def pre_process(): return 3 @python_app def option_one(x): # 何かの処理 return x*2 @python_app def option_two(x): # 何かの処理 return (-x) * 2 @join_app def process(x): if x > 0: return option_one(x) else: return option_two(x) @python_app def post_process(x): return str(x) post_process(process(pre_process()))).result()] もしJoinアプリの機能がない場合は次のようなPythonアプリをコードすることになります。 @python_app def process(x): if x > 0: f = option_one(x) else: f = option_two(x) return f.result() これは option_one()とoption_two() が完了するまでワーカーの実行プロセスをブロックすることになります。 option_one()とoption_two() を実行するのに十分なワーカーが存在しない場合、これはデッドロックになる可能性があります。 Parslでは、アプリは他のアプリの完了を待つようなコードはするべきではありません。常に依存関係を通じてparslに処理させるように留意してください。 データの受け渡し Parslアプリは、Pythonオブジェクトまたはファイルの形でデータを交換できます。データフロー セマンティクスを実施するために、Parsl はアプリに出入りするデータを追跡する必要があります。これらの依存関係をParslに認識させるために、Parslアプリの関数はinputsとoutputsのキーワード引数を与えることができます。 まず、hello1.txt、hello2.txt、hello3.txtという名前の3つのテストファイルを作成します。このファイルには、"hello 1"、"hello 2"、"hello 3"というテキストが含まれています。 In [2]: # %load 07_create_testfiles.py ...: import os ...: ...: cwd = os.getcwd() ...: for i in range(3): ...: with open(os.path.join(cwd, f'hello-{i}.txt'), 'w') as f: ...: c = f.write('hello {}\n'.format(i)) ...: In [3]: 次のようにテスト用の helloファイルが作成されます。 % grep hello hello-[012].txt hello-0.txt:hello 0 hello-1.txt:hello 1 hello-2.txt:hello 2 次に、catコマンドを使ってこれらのファイルを連結するアプリを書いてみましょう。helloファイルのリストを入力し(インプット)、そのテキストを連結してall_hellos.txtという名前の新しいファイルを作成します(アウトプット)。後述するように、catアプリが別のコンピュータで実行される場合には、Parsl Fileオブジェクトを使用してファイルの場所を抽象化します。 In [2]: # %load 08_concentate_testfiles.py ...: import parsl ...: from parsl.app.app import bash_app ...: from parsl.data_provider.files import File ...: ...: config = parsl.load() ...: ...: @bash_app ...: def cat(inputs=[], outputs=[]): ...: infiles = " ".join([i.filepath for i in inputs]) ...: return f'cat {infiles} > {outputs[0]}' ...: ...: cwd = os.getcwd() ...: concat = cat(inputs=[File(os.path.join(cwd, 'hello-0.txt')), ...: File(os.path.join(cwd, 'hello-1.txt')), ...: File(os.path.join(cwd, 'hello-2.txt'))], ...: outputs=[File(os.path.join(cwd, 'all_hellos.txt'))]) ...: ...: # 結果ファイルを読み出す ...: with open(concat.outputs[0].result(), 'r') as f: ...: print(f.read()) ...: hello 0 hello 1 hello 2 In [3]: !cat all_hellos.txt hello 0 hello 1 hello 2 In [4]: Futures Futuresの言葉の意味としては、「未来」や金融デリバティブの「先物取引」をいいます。Parslのプログラミングでは、「未来」の概念に基づいています。 通常のPython関数が呼び出されると、Pythonインタープリタは関数の実行が完了するのを待ち、結果を返します。長時間実行される関数の場合、完了を待つことは望ましくないかもしれません。そうしたときは、関数は非同期に実行される方がよいはずです。 Parslでは 関数を呼び出すと、その関数が計算する「未来の値」を表す Futuresオブジェクトがすぐに返されます。 関数の計算が別の実行スレッドで行われている間、呼び出したスレッドは他の作業を行うことができます。 呼び出し側のスレッドは、後で関数が完了するのを待って、結果を取得することができます。Future は基本的にParslが非同期タスクのステータスを追跡するためのオブジェクトで、ステータスや結果を調べることができるようになっています。 Parslには重要な2種類のFuturesがあります。AppFutures と DataFutures です。これらの2つのタイプのFuturesは、関連していますが、微妙に異なるワークフローパターンの構築を可能にします。 AppFutures AppFuturesは、Parslスクリプトが構築される基本的な構成要素です。Parslアプリの呼び出しはすべてAppFutureを返し、アプリの実行を管理したり、ワークフローを制御したりするのに使われます。 次のコードは、Pythonアプリの結果を待つためにAppFuturesがどのように使用されるかを示したものです。 In [2]: # %load 09_app_futures.py ...: import parsl ...: from parsl.app.app import python_app ...: ...: config = parsl.load() ...: ...: @python_app ...: def hello(): ...: import time ...: time.sleep(5) ...: return 'Hello World' ...: ...: app = hello() ...: ...: # app が終了済みかどうかをチェックする ...: print(f'Done: {app.done()}') ...: ...: # app の結果を出力する ...: # 注意:この呼び出しはブロックされ、app の終了を待つ ...: print(f'Result: {app.result()}') ...: ...: print(f'Done: {app.done()}') ...: Done: False Result: Hello World Done: True In [3]: DataFutures AppFuturesが非同期アプリの実行を表すのに対し、DataFuturesはアプリが生成するファイルを表します。Parslのデータフローモデルでは、データはファイルを介してあるアプリから別のアプリへと流れますが、アプリが必要なファイルの作成を検証し、入力ファイルが作成されたときにその後の依存関係を解決するために、このような構造が必要なわけです。アプリを起動する際、Parslは出力ファイルのリストを指定する必要があります(outputsキーワード引数を使用)。アプリが実行されると、各ファイルのDataFutureが返されます。アプリの実行中、Parsl はこれらのファイルを監視し、1) ファイルが作成されていることを確認し、2) 依存するアプリにファイルを渡します。 In [2]: # %load 10_data_futures.py ...: import parsl ...: import os ...: from parsl.app.app import python_app, bash_app ...: from parsl.data_provider.files import File ...: ...: config = parsl.load() ...: ...: # 入力で与えられた message を出力ファイルに出力するアプリ ...: @bash_app ...: def slowecho(message, outputs=[]): ...: cmdline = f'sleep 5; echo {message} &> {outputs[0]}' ...: return cmdline ...: ...: # 出力ファイルを指定してslowechoを呼び出す ...: outfile = File(os.path.join(os.getcwd(), 'hello-world.txt')) ...: hello = slowecho('Hello World!', outputs=[outfile]) ...: ...: # AppFutureのoutputs プロパティーは、DataFuturesのリストです。 ...: print(hello.outputs) ...: ...: # AppFutures が終了したかチェック ...: print(f'Done: {hello.done()}') ...: ...: # 出力されたDataFutureの内容を、AppTuture の終了時に出力する ...: with open(hello.outputs[0].result(), 'r') as f: ...: print(f.read()) ...: ...: # ここでDataFutures とAppFuture が終了したかを確認 ...: print(hello.outputs) ...: print(f'Done: {hello.done()}') ...: [<DataFuture at 0x7f17277ff610 state=pending>] Done: False Hello World! [<DataFuture at 0x7f17277ff610 state=finished with file <<class 'parsl.data_provider.files.File'> at 0x7f17384499d0 url=/home/iisaka/Tutorial.Parsl/01_Intro/hello-world.txt scheme=file netloc= path=/home/iisaka/Tutorial.Parsl/01_Intro/hello-world.txt filename=hello-world.txt>>] Done: True In [3]: データ管理 Parslはデータフローパターンの実装を可能にするように設計されています。これらのパターンは、アプリケーション間で渡されるデータが実行の流れを管理するワークフローを定義することができます。データフローのプログラミングモデルは、多くのアプリケーションで必要とされる並行処理を、暗黙の並列処理によってシンプルかつ直感的に表現することができるため、人気があります。 ファイル Parslのファイル抽象化は、アプリケーションの実行場所に関わらず、ファイルへのアクセスを抽象化します。アプリ内でParslのファイルを参照する(filepathを呼び出す)と、Parslはアプリが実行されているファイルシステムに対するファイルの位置にパスを変換します。 In [2]: # %load 11_filepath.py ...: import parsl ...: from parsl.app.app import python_app, bash_app ...: from parsl.data_provider.files import File ...: ...: config = parsl.load() ...: ...: # ファイルの内容を別のファイルにコピーするアプリ ...: @bash_app ...: def copy(inputs=[], outputs=[]): ...: cmdline = f'cat {inputs[0]} &> {outputs[0]}' ...: return cmdline ...: ...: # テストファイルの作成 ...: cwd = os.getcwd() ...: c = open(os.path.join(cwd, 'cat-in.txt'), 'w').write('Hello World!\n') ...: ...: # Parslの Fileオブジェクトを作成 ...: parsl_infile = File(os.path.join(cwd, 'cat-in.txt'),) ...: parsl_outfile = File(os.path.join(cwd, 'cat-out.txt'),) ...: ...: # copy アプリへ Parsl の File オブジェクトを渡して呼び出す ...: copy_future = copy(inputs=[parsl_infile], outputs=[parsl_outfile]) ...: ...: # 結果を読み出す ...: with open(copy_future.outputs[0].result(), 'r') as f: ...: print(f.read()) ...: Hello World! In [3]: リモートファイル Parslのファイル抽象化では、リモートでアクセス可能なファイルを表現することもできます。この場合、ファイルのリモートロケーションを使用してFileオブジェクトをインスタンス化することができます。Parsl は依存するアプリを実行する前に、ファイルを実行環境に自動でステージングします。また、Parsl はファイルの場所をローカル ファイル パスに変換して、依存するアプリがローカル ファイルと同じ方法でファイルにアクセスできるようにします。Parsl は Globus、FTP、HTTP、rsync でアクセス可能なファイルをサポートしています。 次のコードは、内容に乱数を含んだ一般にアクセス可能なファイルからFileオブジェクトを作成します。このオブジェクトは、ローカルファイルと同じように sort_numbers() アプリに渡すことができます。 In [2]: # %load 12_remote_file.py ...: import parsl ...: from parsl.app.app import python_app ...: from parsl.data_provider.files import File ...: ...: parsl.load() ...: ...: @python_app ...: def sort_numbers(inputs=[]): ...: with open(inputs[0].filepath, 'r') as f: ...: strs = [n.strip() for n in f.readlines()] ...: strs.sort() ...: return strs ...: ...: unsorted_file = File('https://raw.githubusercontent.com/Parsl/parsl-tuto ...: rial/master/input/unsorted.txt') ...: ...: f = sort_numbers(inputs=[unsorted_file]) ...: print (f.result()) ...: Out[2]: <parsl.dataflow.dflow.DataFlowKernel at 0x110800dc0> ['0', '1', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '2', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '3', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '4', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '5', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '6', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '7', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '8', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '9', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99'] In [3]: ワークフローの構築 Parsl でワークフローを作成してみましょう。他のワークフローシステムとは異なり、Parslはデーターフローモデルに基づいてワークフローを作成します。つまり、アプリ間の制御やデータの受け渡しから、暗黙のワークフローが作成されます。このモデルの柔軟性により、シーケンシャルなワークフローから複雑なネストされたパラレルなワークフローまで、さまざまなワークフローを作成することができます。 以下では、アプリ間でAppFuturesやDataFuturesを渡すことで、さまざまなワークフローを作成することができることを説明します。 シーケンシャルワークフロー あるタスクから別のタスクにAppFutureを渡すことで、単純なシーケンシャル(Sequential)またはプロシージャル(Procedural)ワークフローを作成できます。 次の例は、まず乱数を生成し、それをファイルに書き込む処理をシーケンシャルに実行します。 In [2]: # %load 20_sequential_workflow.py ...: import parsl ...: from parsl.app.app import python_app, bash_app ...: from parsl.data_provider.files import File ...: ...: conf = parsl.load() ...: ...: # 乱数を生成するアプリ ...: @python_app ...: def generate(limit): ...: from random import randint ...: return randint(1,limit) ...: ...: # ファイルに値を書き出すアプリ ...: @bash_app ...: def save(variable, outputs=[]): ...: cmdline = f'echo {variable} &> {outputs[0]}' ...: return cmdline ...: ...: # 1〜10の範囲で乱数を生成 ...: random = generate(10) ...: print(f'Random number: {random.result()}') ...: ...: # 乱数をファイルに保存 ...: outfile = File(os.path.join(os.getcwd(), 'sequential-output.txt')) ...: saved = save(random, outputs=[outfile]) ...: ...: # 結果を出力 ...: with open(saved.outputs[0].result(), 'r') as f: ...: data = f.read() ...: print(f'File contents: {data}') ...: Random number: 9 File contents: 9 In [3]: 並列ワークフロー Parslアプリが並列に実行される最も一般的な処理は、ループを使ったものです。次の例では、単純なループを使って多数の乱数を並列に生成する方法を示しています。 In [2]: # %load 21_parallel_workflow.py ...: import parsl ...: import os ...: from parsl.app.app import python_app, bash_app ...: ...: config = parsl.load() ...: ...: # delay秒遅延して乱数を生成するアプリ ...: @python_app ...: def generate(limit,delay): ...: from random import randint ...: import time ...: time.sleep(delay) ...: return randint(1,limit) ...: ...: # 1〜10の間の乱数を5つ生成する ...: rand_nums = [] ...: for i in range(5): ...: rand_nums.append(generate(10,i)) ...: ...: # アプリの終了を待つ ...: outputs = [i.result() for i in rand_nums] ...: ...: # 結果の出力 ...: print(outputs) ...: [8, 7, 10, 1, 4] In [3]: モンテカルロ法のワークフロー 多くの科学アプリケーションでは、モンテカルロ法を使ったシミュレーション計算を行っています。 このモンテカルロ法を使って円周率の近似値を求めるアルゴリズムは次のようになります。 $ 1 \times 1$ の正方形内にランダムに点を打つ(一様分布にしたがった乱数を使う) 原点から距離が 1 以下なら 1 ポイント,1 より大きいなら 0 ポイント追加 これらの操作を N 回繰り返して総獲得ポイントを P とする $ \dfrac {4P}{N} $ を円周率 $ \pi $ の近似値として得る 関数 pi() の各呼び出しは,独立かつ並列に実行されます.avg_three() アプリは,pi() の呼び出しから返されたFutures の平均を計算するために使用されます. ワークフローは次のようになります。 App Calls pi() pi() pi() \ | / Futures a b c \ | / App Call avg_points() | Future avg_pi In [2]: # %load 30_monte_calro.py ...: import parsl ...: from parsl.app.app import python_app, bash_app ...: from parsl.configs.local_threads import config ...: ...: config = parsl.load() ...: ...: @python_app ...: def pi(num_points): ...: from random import random ...: ...: inside = 0 ...: for i in range(num_points): ...: x, y = random(), random() # ボックスにランダムな点をうつ ...: if x**2 + y**2 < 1: # 円の中にポイントがあるかチェック ...: inside += 1 ...: ...: return (inside*4 / num_points) ...: ...: # 3つの値の平均値を計算するアプリ ...: @python_app ...: def mean(a, b, c): ...: return (a + b + c) / 3 ...: ...: # pi の近似値を求める ...: a, b, c = pi(10**6), pi(10**6), pi(10**6) ...: ...: # 3つの値の平均を求める ...: mean_pi = mean(a, b, c) ...: ...: # 結果の出力 ...: print(f"a: {a.result():.5f} b: {b.result():.5f} c: {c.result():.5f}") ...: print(f"Average: {mean_pi.result():.5f}") ...: a: 3.14092 b: 3.14195 c: 3.14246 Average: 3.14178 In [3]: 実行と設定 Parslは、任意の実行プロバイダ(PC、クラスター、スーパーコンピュータ、クラウドなど)や実行モデル(スレッド、パイロットジョブなど)をサポートするように設計されています。スクリプトの実行に使用されるコンフィギュレーションは、目的の環境でアプリをどのように実行するかをParslに伝えます。Parslは、特定のアプリやスクリプトのリソース構成を記述するために、ブロックと呼ばれる高レベルの抽象化を提供しています。 捕捉:パイロットジョブ 大量のタスクを処理するための一括処理ジョブのこと サポートしている実行プロバイダー LocalProvider::ラップトップやワークステーションでローカルに実行することができるプロバイダ CobaltProvider: Cobalt スケジューラを介してリソースをスケジュールすることができるプロバイダ SlurmProvider: Slurm スケジューラを使ってリソースをスケジュールすることができるプロバイダ CondorProvider: Condor スケジューラーによるリソースのスケジューリングすることができるプロバイダ GridEngineProvider: GridEngine スケジューラを使用してリソースをスケジュールすることができるプロバイダ TorqueProvider : Torque スケジューラでリソースをスケジュールすることができるプロバイダ PBSproProvider : PBSPro スケジューラでリソースをスケジュールすることができるプロバイダ AWSProvider::Amazon Web Servicesのクラウドノードをプロビジョニングおよび管理するためのプロバイダ AzureProvider:Microsoft Azure のクラウドノードをプロビジョニングおよび管理するためのプロバイダ GoogleCloudProvider。Google Cloudからクラウドノードのプロビジョニングと管理を行うことができるプロバイダ KubernetesProvider:Kubernetesクラスター上のコンテナのプロビジョニングと管理を行うためのプロバイダ AdHocProvider: アドホック・クラスターを形成するノードの集合体の実行を管理するためのプロバイダ LSFProvider: IBMのLSFスケジューラを使ってリソースをスケジューリングするためのプロバイダ サポートしているエグゼキューター ThreadPoolExecutor::ローカルリソースでのマルチスレッド実行をサポートします。 HighThroughputExecutor:パイロットジョブモデルを用いた階層的なスケジューリングとバッチ処理を実装し、最大4000ノードでの高スループットなタスク実行を実現します。 WorkQueueExecutor:実行バックエンドとしてWork Queueを統合します。Work Queueは、数万コアの規模に対応し、動的なリソースサイジングによる信頼性の高いタスク実行を実現します。 ExtremeScaleExecutor:mpi4pyを使用して4000以上のノードにスケールアップします。このエクゼキュータは通常、スーパーコンピュータでの実行に使用されます。 サポートしているランチャー SrunLauncher:Slurm ベースのシステム用の Srun ベースのランチャーです。 AprunLauncher:AprunベースのCRAYシステム用ランチャーです。 SrunMPILauncher:Srun で MPI アプリケーションを起動するためのランチャーです。 GnuParallelLauncher:GNU parallelを使用し、ノードやコアをまたいでワーカーを起動するランチャー。 MpiExecLauncher:Mpiexec を使用して起動します。 SimpleLauncher:ランチャーのデフォルトはシングルワーカーの起動です。 SingleNodeLauncher:単一のノード上の workers_per_node カウントのワーカーを起動します。 サポートされているさまざまな実行プロバイダやエクゼキュータに関する詳細な情報は、Parslのドキュメントの Executor を参照してください。 モニタリング Parslには、タスクの状態やリソースの使用状況を長期的に把握するための監視システムが提供されています。Parslの監視システムは、リモートマシン上で実行される個々のアプリに至るまで、プログラムの状態を追跡するのに役立つ詳細な情報と診断機能を提供することを目的としています。 モニタリングシステムは、ワークフローの実行中にSQLiteデータベースに情報を記録します。この情報は、parsl-visualize ツールを使用してWebダッシュボードで可視化したり、通常のSQLiteツールを使用してSQLでクエリを実行したりすることができます。 モニタリングの構成 Parsl のモニタリングは HighThroughputExecutor でのみサポートされています。 次の例は、Parsl構成でモニタリングを有効にする方法を示しています。ここでは、MonitoringHubがポート55055を使用して、10秒ごとにワーカーから監視メッセージを受信するように指定されています。 import parsl from parsl.monitoring.monitoring import MonitoringHub from parsl.config import Config from parsl.executors import HighThroughputExecutor from parsl.addresses import address_by_hostname import logging config = Config( executors=[ HighThroughputExecutor( label="local_htex", cores_per_worker=1, max_workers=4, address=address_by_hostname(), ) ], monitoring=MonitoringHub( hub_address=address_by_hostname(), hub_port=55055, monitoring_debug=False, resource_monitoring_interval=10, ), strategy=None ) 可視化 Web・ダッシュボード・ユーティリティ parsl-visualize を実行するには、追加でモジュールをインストールする必要があります。 $ pip install "parsl[monitoring]" Parsl プログラムの実行中または実行後に Web ダッシュボードを表示するためには、parsl-visualize を実行します。 $ parsl-visualize デフォルトでは、このコマンドは、現在の作業ディレクトリにあるデフォルトのmonitoring.dbデータベースが使用されていることを想定しています。コマンドラインにデータベースのURIを渡すことで、他のデータベースを読み込むことができます。例えば、データベースの絶対パスが tmp/my_monitoring.db の場合は、次のように実行します。 $ parsl-visualize sqlite:////tmp/my_monitoring.db デフォルトでは、arsl-visualize のWebサーバーは 127.0.0.1:8080 でリッスンします。ローカルのマシンで parsl-visualize を起動した場合は、ダッシュボードはhttp://127.0.0.1:8080 アクセスできます。Webサーバがクラスタのログインノードなどのリモートマシンに配置されている場合、ローカルマシンからクラスタへのSSHトンネルを使用する必要があります。 $ ssh -L 50000:127.0.0.1:8080 username@cluster_address このコマンドを実行すると、ローカルマシンのポート50000がリモートクラスタのポート8080にバインドされます。ダッシュボードには、ローカルマシンのブラウザからhttp://127.0.0.1:50000 でアクセスできます。 また、可視化サーバーをパブリックインターフェースに配置することもできます。ただし、クラスタのセキュリティポリシーで許可されているかどうかをまず確認してください。次の例では、Webサーバーをパブリックポート(public_IP:5555を介してインターネットに公開)に配置する方法を示しています。 $ parsl-visualize --listen 0.0.0.0 --port 55555 ダッシュボード ダッシュボードのワークフロー ページには、選択したデータベースで監視を有効にして実行されたすべてのParslワークフローが一覧表示されます。ワークフローの状態を高レベルで要約したものが以下のように表示されます。 ダッシュボード全体を通して、青色の要素はすべてクリック可能です。例えば、表の中の特定のワークフロー名をクリックすると、次のセクションで説明する「Workflow Summary」ページに移動します。 ワークフローサマリー ワークフローサマリーページでは、開始時間、終了時間、タスクサマリー統計など、ワークフローの実行レベルの詳細が表示されます。ワークフローサマリーの後には、App Summary が表示され、様々なアプリとそれぞれの起動回数が表示されます。 また、ワークフローサマリーでは、ワークフローの3つの異なるビューが表示されます。 ワークフローのDAG アプリを色で区別 この表示は、ワークフローの依存関係の構造を視覚的に確認するのに便利です。DAG内のノードにカーソルを合わせると、そのノードで表されるアプリとそのタスクIDのツールチップが表示されます。 ワークフローDAG - タスクの状態を色で区別 この可視化は、どのタスクが完了したのか、失敗したのか、現在保留になっているのかを識別するのに役立ちます。 ワークフローのリソース使用状況 ワークフローレベルでのリソース使用状況を可視化します。例えば、ワーカー全体のCPU/メモリ使用率の累積を時間軸で表示します。 まとめ 多数のデータを扱ったり、シミュレーションを繰り返す場合などでは、特定の分野に特化したワークフローを構築することになります。これらのワークフローは、さまざまな独立したソフトウェア機能や外部アプリケーションを統合したものになります。しかし、多くの場合このようなワークフローを開発・実行・保守することは難しく、アプリケーションやデータの複雑なオーケストレーションや管理、特定の実行環境に合わせたカスタマイズが必要になります。そしてこうした作業は実行環境がリプレースなどで変わるたびに繰り返されることになります。 Parslではコードと実行環境が分離されているため、PythonスクリプトにParslディレクティブをアノテーションするだけで、ラップトップからクラスター、クラウド、グリッドなどのリソース上でスクリプトの実行をすることができるようになります。 Parslを使用することで、必要なデータの移動をオーケストレーションし、Python関数や外部アプリケーションの並列実行の管理が容易になります。 参考 Parsl オフィシャルサイト Parsl: Pervasive Parallel Programming in Python Parsl: Pervasive Parallel Programming in Python & funcX: A Federated Function Serving Fabric for Science
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python環境構築法 for Windows10

新しいPCが届き,Anacondaを使わずにPython環境の構築を1からやり直したので,整理も兼ねて投稿します. 対象者 Microsoft StoreからPythonをインストールしたくない人.(ここ重要) Anacondaを使いたくない人. Pathを通す作業をなるべくしたくない人. pyenvとpipで開発したい人. 事前準備 chocolateyがインストールされていること. chocolateyのパスが通っており,動作を確認済み. やり方 pyenvのインストール cinst pyenv-win でpyenvをインストールできます. 何か聞かれた時はこれを参照 何か聞かれた時 Do you want to run the script?([Y]es/[A]ll - yes to all/[N]o/[P]rint) という表示があればYesもしくはAllと入力してください. インストール可能なPythonのバージョンを探す pyenv install -l 出力例 出力例 ... 3.9.1rc1-win32 3.9.1rc1 3.9.1-win32 3.9.1 3.9.2rc1-win32 3.9.2rc1 3.9.2-win32 3.9.2 3.9.3-win32 3.9.3 3.9.4-win32 3.9.4 3.9.5-win32 3.9.5 3.9.6-win32 3.9.6 3.10.0a1-win32 3.10.0a1 3.10.0a2-win32 ... 調べていないので確証はありませんが 3.9.6-win32は32bitのWindows用のPythonがインストールされるのではないかと思います. とりあえず,バージョンの後に何もついていないものを選べば良いと思います. 好きなバージョンをインストール(例として3.9.6をインストール) Pythonのインストール pyenv install 3.9.6 成功例 成功例 :: [Info] :: completed! 3.9.6 と出ていれば成功です! おめでとう!! 失敗した時はこちらを参照 rehash これでもう一度試してください. インストールできたか確認 pyenv versions でインストールされているPythonが出力されます. 成功例 * 3.9.6 と出ていれば正常にインストールされています. ちなみに*が出ているバージョンが現在,適用されているバージョンです. 実行できるか確認 Pythonの実行確認 python -V # 出力:Python 3.9.6 となっていれば成功です. Microsoft Storeが立ち上がる ここで初めて環境変数をいじります. 環境変数ウィンドウを開く. ユーザー環境変数のPathを選択後,編集ボタンをクリック. %USERPROFILE%/.pyenv/pyenv-win/shimsと%USERPROFILE%/.pyenv/pyenv-win/binを一番上に持ってくる. ↑の2つの値が無い時はこちらを参照 cinst pyenv-winの時点でなぜかPathに追加されなかった様なので,Pathを直接編集します. ~/.pyenv/pyenv-win直下にshimsとbinがあることを確認. ユーザー環境変数のPathを選択後,編集ボタンをクリック. 新規ボタンをクリック後,%USERPROFILE%/.pyenv/pyenv-win/shimsと入力. もう一度新規ボタンをクリック後,%USERPROFILE%/.pyenv/pyenv-win/binと入力. どちらも%USERPROFILE%/AppData/Local/Microsoft/WindowsAppsより上に移動させる. Pythonのバージョンを切り替える バージョンの切り替えにはglobalとlocalの2種類があります. globalはシステム全体を通して扱うPythonのバージョンを指定できます. localは任意のディレクトリ直下で扱うPythonのバージョンを指定できます. localで指定したバージョンはglobalよりも優先されます. コマンドは以下の通りです. Pythonのバージョン切り替え pyenv global 3.9.5 # または pyenv local 3.9.5 コマンド実行後にpython -Vを実行して,指定したバージョンが出力されていれば成功です! 仮想環境を作ろう!そして入ろう! 続いて仮想環境を作ってそこに入ってみましょう! 仮想環境を作る cd ~/work_space #好きなディレクトリで良い pyenv local 3.9.6 #好きなバージョン ls -a ./ ../ .python-version #.python-versionにバージョンがメモされる.(これを修正してもバージョンは変わらない) python -m venv .env #.envは隠しフォルダ名なので好きな名前で良い ls -a ./ ../ .env/ .python-version #.envがあれば成功! 以上で仮想環境の作成が完了です. 仮想環境に入る #Git Bashの場合 source .env/Scripts/activate #仮想環境の有効化 #PowerShellの場合 ./.env/Scripts/Activate.ps1 #仮想環境の有効化 最後のコマンド実行後 (.env)と出ていれば仮想環境に入れています!おめでとう!! 仮想環境から出る時は,Git BashもPowerShellも 仮想環境から出る deactivete で出れます. Q&A Chocolateyって? ChocolateyとはCUIでソフトウェアやコマンド類をインストールできるWindows用のパッケージマネージャーです. 簡単に言えば,コマンドでインストールやアンインストールするApp StoreやGooglePlay Storeです. インストールするにはPowerShellを管理者権限で起動し,ここにアクセス.真ん中あたりにコピペで動かせるコマンドがあるので,それをコピペ実行で入ります. Chocolateyを略してChocoと表現したりします. pyenvって? pyenvとはPython用のバージョン管理ツールです.これを使うと様々なバージョンを扱うことができ,公式ページから落として環境変数をいじる必要がなくなります.楽過ぎますね〜 Rubyでいうrbenv,Node.jsならnvmのことです. Dockerじゃあかんの? ただのPython環境で実行するのならそれで良いと思います.楽ですしね. ただ,CUDAを使いたい場合,現時点(2021/10/13)では楽に環境構築ができません. CUDA on Dockerを実現するくらいなら仮想環境の方が楽なので,こちらを推します. pyenvで入れた特定のバージョンを消したい pyenvで入れたバージョンを消す pyenv uninstall 3.9.5 これで消えます. Chocoで入れたパッケージを消したい Chocoのパッケージを消す chocolatey uninstall PackageName #または choco uninstall PackageName #または cuninst PackageName これで消えます.ただ,pyenvで入れたものまで消えてくれるかは未確認です. pyenv uninstallで消してからcuninstするのが無難です. Chocoで入れたパッケージを確認したい Chocoで入れたパッケージを確認する Chocolatey list -localonly #または choco list -localonly #または clist -localonly でババーと入れたものが出てきます. 後書き Pythonの環境構築のやり方は沢山あり,どれが良いかは個人によります.(悪手はありますね... Microsoft Storeから落とすとか) 今回のやり方は一番ではありませんが,個人的に一番シンプルで覚えることも少ないと思っています. もちろんAnacondaを使えば同じこともできますが,こっちの方が重くないのでおすすめです. お願い この記事の内容で?となった部分や記載されていない不具合についてコメントをください. わかる範囲でQ&Aに追記して答えていこうと思います!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GPUを活用した機械学習ツールNVIDIA RAPIDSをArch Linux + CUDA11.4 でビルドし、Cupy にも組み込んだ(Ver21.10版) (3) CUgraph

やったこと=RAPIDS のビルド データ処理、機械学習のフレームワークRAPIDSを Arch Linux でビルドした。ただし、ビルドまでにエライ手間がかかったので、皆さんへの共有も兼ねて 0. 基本構成その0 ・・・ Cupyのビルド方法についてはこちらの記事で紹介してます。 1. 基本構成その1 ・・・ こちら。RMM, cuDFというRAPIDS の基本コンポーネントの一部のビルド手順。 2. 基本構成その2 ・・・ こちら。CUMLというRAPIDS の基本コンポーネントの一部のビルド手順。ついでに、cuSignal という信号制御ライブラリやcuxfilter という描画データ変換ライブラリも 3. 基本構成その3 ・・・ いまここ。グラフを扱うCUGraphのビルド手順。 cuGraph って何? NVIDIAから提供されている、グラフ分析用ライブラリ。上述のgithubやドキュメントのとおり、RAPIDS経由で提供されている. 事前想定 NVIDIA GeForce RTX/GTX を持っている人 CUDA含めた開発環境が揃っている。 Arch Linux の使い手。EndeavourOSなど派生ディストリビューションでもOKだが、Manjaro のように独自リポジトリを持ち、カーネル、ドライバなどのリリースのペースが異なるものは関知しません。 yay とか PKGBUILD とかが何やってるかが、ある程度わかること。 ビルド方法 GitHubリンクに、PKGBUILDとかビルド順とか。で済ませたいですが、補足説明を。あと参考までにビルドの依存関係をつけます。 0 事前に、pacman で spdlog をインストールしてください。外部でspdlogを持つ強い根拠はないです。別記事のとおりRAPIDSの他コンポーネントのビルド設定と合わせただけです。 sudo pacman -S spdlog その後、 /etc/makepkg.cfg のコンパイルフラグに、「-DSPDLOG_FMT_EXTERNAL」というフラグをつけて、SPDLOGはOSにインストールしたほうを呼び出す設定にしてください。 CFLAGS="-march=skylake-avx512 -mtune=native -DSPDLOG_FMT_EXTERNAL -O3 -pipe -fno-plt -fexceptions \ -Wp,-D_FORTIFY_SOURCE=2,-D_GLIBCXX_ASSERTIONS \ -Wformat -Werror=format-security \ -fstack-clash-protection -fcf-protection" 1. cuGraph GITにあるファイルをローカルに置いて、ビルドすればOK。ただし、現行バージョン(21.10)の制限は、 - 他のコンポーネント(CUML,CUDF)はGCC11でビルドできるが、CUGraphはGCC11ではビルドできない。GCC10を使う必要がある。cugraph でなくそこから呼び出している外部のライブラリがGCC11でビルド不可のため。 というのがある。あとは、test中のコマンドなどで動作を確認してほしいが、CPU vs GPU の動作比較となるため、networkXのインストールも必要になる。 sudo pacman -S python-networkx Cupy に cuGraph を組み込み なんのことはなく、cuGraphをインストール後に、Cupyを再ビルドすれば、cuGraph も自動的に組み込まれる Cupy 基本的には以前の投稿と同じ(cuSparseLtは無くてもよい)。 ただし、パッチとして、 * cugraph ヘッダファイルの位置の訂正 * cugraph の 21.10 から一部ライブラリが、Legacyに移っている参考ためその対応をしている。 記録 (2021.10.13 23:00 JST)...初版 おことわり この記事は、筆者以外の方の環境でも同様に成功することを保証するものではありません。ビルドや動作確認の失敗、(ないとは思いますが)環境が破壊されても責任は一切負いかねます(基本的には自己責任でお願いします。)。 今回紹介したリンク https://github.com/gdaisukesuzuki/PKGBUILD_rapidsai2110
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GraphCMSとHugoを連携してGithub Pagesで公開する(番外編)〜国際化対応

概要 前回まででHugoとGraphCMSを連携させてGithu Pagesで公開する方法を解説した。 今回、番外編として国際化対応の実装を行う。 国際化対応 ウェブ制作の案件として国際化対応についてはどれほど需要があるかわからないが、HugoもGraphCMSもi18nを簡単に導入できるので説明に加えることにした。 GraphCMSの設定 GraphCMSはフリープランでは二カ国語まで対応している。 デフォルトがenなのでまず日本語のjaを加える。 ロケールの追加設定 ダッシュボード>Settings>Localesを開く。 Dispyal nameにJapaneseを選びAddをクリック。 JapaneseとJapanese(Japanese)と二つあってややこしいが、クエリー結果の出力名称(API名)の違いである。 Japanese: ja Japanese(Japanese): ja_JP なのでその後のスクリプト内での表記法が異なるだけなので別にどちらをでもよい。 ただし、今回はこの名称をHugoの言語別のディレクトリ名に使うのでen, jaとなるほうを採用した。 スキーマ定義修正 設定にロケールを追加しただけでは何も変わらない。 モデルのフィールド毎にロケールの設定を行う必要がある。 postモデルの場合、多言語化対象になるのはtitle, body, tagの3つである。 slugやdateなどは言語が関係ないので修正不要である。 まずtitleを修正する。 titleのEdit fieldをクリックしLocalized fieldにチェックをいれる。 同様に、bodyやtagも設定する。 追加設定 ここで追加の設定を行う。 ブログを多言語化した場合、必ずしも二ヶ国語で投稿したいとは限らない。 国際的な話題は二ヶ国語化したいが、基本は日本語だけで十分だと言う場合。 逆に英語でお知らせしたいが日本語は不要の場合が考えられる。 そのためモデルのスキーマ定義に言語設定用のフィールドを追加して出力する言語を選べるようにする。 スキーマ定義の左のEnumerations>Addをクリック。 Nameをlocale_listとしてjaとenを加えた。 そしてpostモデルのフィールドにDropdownとして追加する。 最終的なスキーマ定義はこんな感じ。 invalid_localeは選択された言語をビルドの対象から外すオプションだ。 必須項目とはせず、jaを選択したら日本語、enを選択したら英語を対象外とする。 デフォルトでは何も選択しないのでその場合は両方出力となるる。 コンテンツ修正 このままではまだ国際化対応はできていない。 今度は既存のコンテンツを修正する。新規に作る場合も要領は同じ。 右側にLOCALIZATIONSと表示されているところがありEnglishのみがチェック入っている。 Japaneseも同様にチェックすると、Localized対象のフィールドのtitleなどが該当言語ごとに追加される。 このままではデフォルトのenに日本語が入っているので面倒だが、jaのところに移す。 invalid_localeでenを選択すると英語は不要になるのだけどもtitleなどのフィールド自体は必須項目なので何か入れる必要がある。 確認 ダッシュボードのAPI Playgroundから以下のクエリーをなげて確認する。 query MyQuery { posts(locales: [ja, en], orderBy: updatedAt_DESC) { localizations(includeCurrent: true) { id title slug date eyecatch { url } body tag locale invalidLocale updatedAt } } } Hugoの設定 続いてHugoの設定。 config.tomlに言語設定を追加する # 省略 [languages] [languages.en] title = "My blog" weight = 2 contentDir = "content/en" [languages.ja] title = "私のブログ" weight = 1 contentDir = "content/ja" # 省略 上記でわかる通り出力ディレクトリが変わるので以下のような構成にする。 content ├── en │   ├── _index.md │   └── post │   └── _index.md └── ja ├── _index.md └── post └── _index.md Pythonの設定 受け入れ体制が整ったのでスクリプトの修正を行う。 細かい解説はしないので完成形をお見せする。 app/__main__.py import datetime import json import os import pathlib import re import urllib.request from dotenv import load_dotenv APP_DIR = os.path.abspath(os.path.dirname(__file__)) PROJECT_DIR = pathlib.Path(APP_DIR).parent HUGO_CONTENT_DIR = os.path.join(PROJECT_DIR, 'content') HUGO_POST_DIRNAME = 'post' UPDATE_SEC = int(os.getenv('UPDATE_SEC', '300')) dotenv_path = os.path.join(PROJECT_DIR, '.env') load_dotenv(dotenv_path) class GraphcmsManager(object): def __init__(self, endpoint, token): self.endpoint = endpoint self.headers = {'Authorization': f'Bearer {token}'} def __format_query(self, s): s = re.sub(r'\s+', '' ' ', s).replace('\n', ' ') return {'query': f'{s}'} def __query_statement(self): return '''\ { posts(locales: [ja, en], orderBy: updatedAt_DESC) { localizations(includeCurrent: true) { id title slug date eyecatch { url } body tag locale validLocale updatedAt } } }''' def query(self, data=None, is_raw=True): if not data: data = self.__query_statement() if is_raw: data = self.__format_query(data) req = urllib.request.Request(self.endpoint, data=json.dumps(data).encode(), headers=self.headers) status_code = 500 try: with urllib.request.urlopen(req) as response: payload = json.loads(response.read()) status_code = response.getcode() except urllib.error.HTTPError as e: payload = {'error': e.reason} except urllib.error.URLError as e: payload = {'error': e.reason} except Exception as e: payload = {'error': str(e)} return status_code, payload def __time_diff(self, date_str, fmt='%Y-%m-%dT%H:%M:%S.%f+00:00'): updatetime = datetime.datetime.strptime(date_str, fmt) return (datetime.datetime.now() - datetime.timedelta(hours=9) - updatetime).seconds def gen_hugo_contents(self, payload): result = list() data = (payload.get('data')) for model, content_list in data.items(): for content in content_list: for x in content.get('localizations'): data_map = dict() locale = x['locale'] if locale == x.get('validLocale') print(f'Pass language code {locale} for content {x["id"]}') continue front_matter = f'title: "{x["title"]}"\n' front_matter += f'slug: "{x["slug"]}"\n' front_matter += f'date: {x["date"]}\n' eyecatch = x.get('eyecatch') if eyecatch: front_matter += f'featured_image: {eyecatch["url"]}\n' tag = x.get('tag') if tag: front_matter += f'tags: {str(tag)}\n' data_map['front_matter'] = front_matter data_map['body'] = x['body'] data_map['filepath'] = f'{x["id"]}.md' data_map['update_sec'] = self.__time_diff(x['updatedAt']) data_map['locale'] = locale result.append(data_map) return result def write(self, data, update_sec=UPDATE_SEC): for x in data: fullpath = os.path.join(HUGO_CONTENT_DIR, x['locale'], HUGO_POST_DIRNAME, x['filepath']) os.makedirs(os.path.dirname(fullpath), exist_ok=True) if os.path.exists(fullpath) and x["update_sec"] > update_sec: # skip old posts continue with open(fullpath, 'w') as f: text = f'---\n{x["front_matter"]}---\n{x["body"]}' f.write(text) def main(): endpoint = os.getenv('GRAPHCMS_ENDPOINT', 'http://localhost') token = os.getenv('GRAPHCMS_TOKEN', 'my-token') G = GraphcmsManager(endpoint=endpoint, token=token) status_code, payload = G.query() if status_code != 200: print(payload) return data = G.gen_hugo_contents(payload) G.write(data) if __name__ == "__main__": main() コンテンツ取得、ビルドおよび開発サーバ起動 python -m app hugo serve 結果 まずトップページはこんな感じになる。 右上にenのメニューが追加されていることを確認していただきたい。 そしてこのenをクリックするとページは英語表記に変わり言語メニューはjaとなる。 ブログ詳細ページも同様に言語メニューが出るのだが、先程日本語のみの出力設定(invalid_localeにenを設定)を行ったコンテンツはこんなふうになり、日本語でしか表示されない。 そもそも先に紹介したトップページの日本語画面、英語画面のところに表示されるブログの一覧のところで既に違いが出ている。 完成したソース 上記で作成したサイト まとめ HugoとGraphCMSとの連携の番外編として国際化の説明を行った。 実際の業務案件としてウェブサイトをリニューアルして二ヶ国語対応したいというのがあり、Hugoでやってみたいと思ったのがきっかけだった。 Hugoでやる場合、ネックになるのがMarkdown記法である。私は慣れているが一般的な利用者向きとはいえず、テキストエディタで投稿するのは現実的ではなかった。 そこで目をつけたのがヘッドレスCMS。 最初はContentfulを使ってみた。以前紹介したcontentful-hugoというライブラリを利用すれば連携できることはわかった。 しかし、Node.jsで書かれたcontentful-hugoのカスタマイズに苦労したり、またCotentful側のAPIのi18n対応が思った結果を取得できなかったことからいろいろ模索してGraphCMSにたどり着いた。さらにPythonでスクリプトを書くことで自分の土俵にもってくることができた。 ヘッドレスCMSのWEB-APIを利用してコンテンツを取得する 上記をMarkdown形式で書き出す というたった二つのことを具象化するだけでHugoとヘッドレスCMSの相乗効果を引き出せるのはすごいことだと思う。 今回紹介したGraphCMS(と以前紹介したContentful)に限らずいろんなヘッドレスCMSで同様のことが実現できると思われるのでもっとこういった活用事例が増えるとみんなハッピーになれるかなと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

相互作用特徴量

この記事の狙い・目的 機械学習を取り入れたAIシステムの構築は、 ①データセット作成(前処理)→ ②モデルの構築 → ③モデルの適用 というプロセスで行っていきます。 その際「データセット作成(前処理)」の段階では、正しくモデル構築できるよう、事前にデータを整備しておくことが求めらます。 このブログでは、その際用いられることのある「相互作用特徴量」の生成方法について解説していきます。 プログラムの実行環境 Python3 MacBook pro(端末) PyCharm(IDE) Jupyter Notebook(Chrome) Google スライド(Chrome) 相互作用特徴量 相互作用特徴量の生成方法について、解説したいと思います。 相互作用特徴量とは、二つ以上の変数をかけ合わせた新しい変数を作る方法のことです。 特に、二つの特徴量の積で表された特徴量を「ペアワイズ交互作用特徴量」と言います。 メリット 二つ以上の特徴量の組み合わせにより、目的変数をうまく表現できる場合、単一の特徴量よりモデルの精度が高まる場合があります。 デメリット 元の項目数が「$n$個」の場合、特徴量の生成後はの項目数は「$n^2$乗個」となる為、学習コスト増大します。 これを回避する方法として、特徴選択があります 以下の結果では、精度の向上が見られはしますが、非常に微々たる変化です。特徴量を精査すれば更に改善が可能でしょう。 from sklearn.ensemble import RandomForestClassifier # ランダムフォレスト from sklearn.metrics import accuracy_score, recall_score, precision_score, f1_score # 各評価指標 from sklearn.metrics import confusion_matrix # 混同行列 import sklearn.preprocessing as preproc from sklearn.model_selection import train_test_split def learning(X_train, X_test, y_train, y_test): model = RandomForestClassifier(random_state=42) model.fit(X_train, y_train) y_pred = model.predict(X_test) # 評価 print('Accuracy = ', accuracy_score(y_true=y_test, y_pred=y_pred).round(decimals=3)) print('Precision = ', precision_score(y_true=y_test, y_pred=y_pred).round(decimals=3)) print('Recall = ', recall_score(y_true=y_test, y_pred=y_pred).round(decimals=3)) print('F1 score = ', f1_score(y_true=y_test, y_pred=y_pred).round(decimals=3)) print('='*20) # 混同行列 y_pred = model.predict(X_test) matrix = plot_confusion_matrix(y_pred, y_test) return matrix def plot_confusion_matrix(predict, y_test): pred = np.where(predict > 0.5, 1, 0) cm = confusion_matrix(y_test, pred) matrix = pd.DataFrame(cm) matrix.columns = [['予測_負例(0)', '予測_正例(1)']] matrix.index = [['実際_負例(0)', '実際_正例(1)']] return matrix # 特徴選択 Interaction = df.copy() features = ['残高', 'クレジットカードスコア'] X1 = Interaction[features] y = Interaction['退会区分'] # ペアワイズ交互作用特徴量 X2 = preproc.PolynomialFeatures(include_bias=False).fit_transform(X1) print(f'生成前の列数: {X1.shape[1]}') print(f'生成後の列数: {X2.shape[1]}') print('='*20) # 目的変数の抽出、データ分割 X1_train, X1_test, X2_train, X2_test, y_train, y_test = train_test_split(X1, X2, y, test_size=0.3, random_state=42) # 学習、評価 matrix1 = learning(X1_train, X1_test, y_train, y_test) matrix2 = learning(X2_train, X2_test, y_train, y_test) matrix1 matrix2 まとめ 今回は相互作用特徴量の生成方法について、簡単に触れてきました。他にも多くの手法がるため、別途紹介とさせていただきます。また相互作用特徴量の精度評価も別記事で取り上げさせていただきます。 解析結果 実装結果:GitHub/churn_modeling.ipynb データセット:Churn Modelling classification data set - kaggle 参考資料
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【深層学習】活性化関数ReLUについて

深層学習モデルを構築する時、うまく活性化関数を選ぶことは大事です。 その中で、よく使われている活性化関数ReLUについて紹介したいと思います。 ReLUとは? ReLUはRectified Linear Unitの頭文字から取ってきたものです。 数式を書くと f(x) = max(x, 0) 数式により、$x$が負数の場合であれば、 $f(x)$は$0$のままということがわかります。 Pythonで書いてみると def relu(x) : return max(x, 0) ReLUの微分 $x$が正数の場合であれば: \frac{\mathrm{d} f(x)}{\mathrm{d} x}=\frac{\mathrm{d} x}{\mathrm{d} x}=1 $x$が$0$以下の場合であれば: \frac{\mathrm{d} f(x)}{\mathrm{d} x}=\frac{\mathrm{d} 0}{\mathrm{d} x}=0 Pythonのif文で書いてみると def relu_derivative(x): if x > 0: return 1 if x <= 0: return 0 ReLUのグラフを作成 import numpy as np import matplotlib.pyplot as plt # inputデータ作成 x = np.linspace(-10, 10, 100) plt.figure(figsize=(8,6)) plt.plot(x, list(map(lambda x: relu(x), x)), label="ReLU") plt.plot(x, list(map(lambda x: der_relu(x), x)), label="Derivative of ReLU") plt.title("ReLU") plt.legend() plt.show() オレンジの線を見ると、 $x>0$の時、微分の数値は$1$で、 $x\le 0$の時、微分の数値は$0$です! 以上、簡単にメモしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AtCoder】ABC222をPython3で解説(ABCD)

ABC222のA-D問題の解説。 目次 A - Four Digits B - Failing Grade C - Swiss-System Tournament D - Between Two Arrays A - Four Digits 必要な知識 zfill() f文字列 解説 与えられた整数を4桁になるように、0を加える問題。 Pythonでは、指定した桁数を0で埋めてくれるzfill()というものがあるので、こちらを使うとかんたんにACできる。 コード1 def main(): n = input() print(n.zfill(4)) if __name__ == '__main__': main() また、f文字列を使う方法もある。 コード2 def main(): n = int(input()) print(f'{n:04d}') if __name__ == '__main__': main() B - Failing Grade 必要な知識 for (filter) 解説 学生の点数aから、P未満の点数をとった人数を数える問題。 これは、forとifを使うことでACできる。 コード1 def main(): n, p = map(int, input().split()) alist = list(map(int, input().split())) cnt = 0 for a in alist: if p > a: cnt += 1 print(cnt) if __name__ == '__main__': main() また、filter()を用いても求めることができる。 コード2 def main(): n, p = map(int, input().split()) alist = list(map(int, input().split())) ans = len(list(filter(lambda x: x < p, alist))) print(ans) if __name__ == '__main__': main() C - Swiss-System Tournament 必要な知識 sort() 二重ループ 2次元配列 解説 1位, 2位 と 3位, 4位...と連続する順位の人同士でじゃんけんさせて、勝利した方に+1ポイントをあたえる。その後、「勝数が多い」または「番号が小さい」人を上位として並び替えをし、同じことを繰り返す。 連続する人同士のじゃんけんは2倍したインデックス番号とそれに1を足したインデックス番号の手を参照してくれば良い。 問題なのが並び替えだ。勝数が多い、はかんたんに並び替えできるが、番号が小さいとなると同時に並び替えができなくなる。ここで、番号が小さい順番にソートする頃を前提に、勝数が多い順にするのではなく、勝数をマイナスの値にして並び替えすると、どちらも小さい順番に並び替えするだけでいい。 コード def main(): n, m = map(int, input().split()) alist = [] for i in range(2*n): temp = list(input()) alist.append(temp) person = [[0, i] for i in range(2*n)] for i in range(m): person.sort() for j in range(n): right = alist[person[2*j][1]][i] left = alist[person[2*j+1][1]][i] if right == 'G' and left == 'C': person[2*j][0] -= 1 elif right == 'C' and left == 'P': person[2*j][0] -= 1 elif right == 'P' and left == 'G': person[2*j][0] -= 1 elif right == left: pass else: person[2*j+1][0] -= 1 person.sort() for i in range(2*n): print(person[i][1]+1) if __name__ == '__main__': main() D - Between Two Arrays 必要な知識 DP 累積和 (MODで割ったあまり) 解説 与えられたaとbの間の数字から、[1, 2, 3, 3, 4, ...]のような右側に一方的に増加する数列は何個あるかを求める問題。 普通に全通りやろうと思うと、最悪計算量が$3000^{3000}$で確実に間に合わない。そこで、DPを活用する。今回は、1, 2, 3, ..のように単調に増加するかどうかだけを判断すればいいので、ひとつ前の値がわかれば、答えを導き出すことができる。 コードの解説をする。まず、DPの配列、そして、その次の値を保持する配列nxtを用意する。一番外側のループは、Cの配列の長さ分回す。次のループは、aからbの値に対して、後に求める1つ前の累積和dpをみて、aからbに当たる値をnxtに保存する。この配列をdpとして渡し、累積和をとる。ここで累積和を取る理由は、先程にも述べたとおり、1つ前までの値を参照すれば良いので、その値までの値の個数で答えとなるCの数列の個数が決まるからである。 最終的な答えは、Cの一番最後の値を答えてあげればAC。 コード def main(): MOD = 998244353 n = int(input()) alist = list(map(int, input().split())) blist = list(map(int, input().split())) m = 3000 dp = [1] * (m+1) for i in range(n): nxt = [0]*(m+1) for j in range(alist[i], blist[i]+1): # 1つ前の累積和を見て # aからbに当たる値をnxtに保存 nxt[j] = dp[j] dp = nxt # 累積和 # 1つ前の値までに値が何個あるかで数列の個数が決まる for i in range(m): dp[i+1] += dp[i] dp[i+1] %= MOD print(dp[m]) if __name__ == '__main__': main() 編集後記 DPの解説記事をおいおい出そうかな 自分でもまだ全然なれてない部分がある...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker + VSCode上に機械学習/深層学習 + Pythonの環境構築する方法(コード整形, lint, 補完あり / Jupyter Notebookのプロセスなし)

ネット上の記事を探していても以下のことを達成する環境構築に触れている記事がなかったのでメモとして残しておきます。 VSCode = Docker の動作環境にしたい 逆にVSCode以外にリソースを割きたくない Jupyter LabやJupyter Notebookのプロセスを立ち上げたくない VSCodeのコード実行機能を使いたい Pythonの補完を最大限使いたい Jupyter Notebookブラウザで使うと補完弱すぎて泣きそうになるので Pythonの静的型チェックやlinter, formatterを良い感じに使いたい Pythonのコード汚くなりがちなので Docker Imageで使える機械学習/深層学習ライブラリを選択したい sklearn用のimageやtensorflow用のimageなど TL;DR 以下のGitHub repositoryをCloneしてVSCodeで開けばOKです。 (余計なファイルも sample.py しか入ってません) $ git clone git@github.com:shierote/python-ml-on-docker-vscode.git $ cd python-ml-on-docker-vscode $ code . 0-1. VSCodeの環境構築 VSCodeのインストール 0-2. Dockerの環境構築 Docker Desktopのインストール 1. VSCodeに Remote - Containers Extensionを入れる VSCodeへ拡張機能を入れる方法 Remote - Containers 2. VSCodeでDockerを使う設定 Docker $ cd /path/to/proj $ touch Dockerfile $ touch docker-compose.yml 以下は anaconda(numpyとかpandasとかがあらかじめ入っているやつ)のimageを利用しています。 別の環境を使いたい場合は後述しています。 Dockerfile FROM continuumio/anaconda3 RUN python -m pip install -U flake8 autopep8 ipykernel docker-compose.yml version: "3" services: dev: init: true build: . volumes: - .:/workspace:cached command: /bin/sh -c "while sleep 1000; do :; done" Remote - Containers $ cd /path/to/proj $ mkdir .devcontainer $ touch .devcontainer/devcontainer.json devcontainer.json { "name": "好きな名前", "dockerComposeFile": [ "../docker-compose.yml", ], "service": "dev", "workspaceFolder": "/workspace", "settings": {}, "extensions": [ "ms-python.python", "streetsidesoftware.code-spell-checker", "oderwat.indent-rainbow" ] } VSCode $ cd /path/to/proj $ mkdir .vscode $ touch .vscode/settings.json setting.json { "python.linting.enabled": true, "python.linting.flake8Enabled": true, "python.linting.flake8Args": [ "--ignore=E111, E114, E402, E501" ], "python.formatting.provider": "autopep8", "python.formatting.autopep8Args": [ "--indent-size=2", "--ignore=E111, E114, E402, E501" ], "python.pythonPath": "/home/ubuntu/python3-venv/bin/python", "python.languageServer": "Pylance", "[python]": { "editor.formatOnSave": true, }, "cSpell.words": [], } 3. VSCodeでDockerを使ってみる プロジェクトのディレクトリをVSCodeで開きます。 すると "Reopen in Container" とポップアップがでるのでクリックします。 でなかった場合は 左下の > <のような青いマークをクリックして "Reopen in Container" をクリックします。 これで自動的にContainerのbuildが始まります。 buildが終われば(左下が Dev Container: .. のような表示になれば) Containerの環境でVSCodeが使えます!! ちなみに図の # %% というsyntaxで .py ファイルをJupyterのように各セル単位で実行できます。 例は sample.py 参照してみてください。 そちらの解説は こちらの記事 に詳しく書いてあります。 【画像で説明】VSCodeをJupyter化する ~.pyファイルをセル単位で実行~ - Qiita flake8 や autopep8 をインストールして、.vscode/settings.json で設定しているので、Pythonコードが綺麗に整形されたり、使われてない変数にWarningが出せると思います!! 使いたくなければ pip install せず、.vscode/settings.json の設定も消してください。 4. カスタマイズするには? pip install したい場合 上図の TERMINAL でpip commandを実行すればOKです。 /workspace# pip install numpy /workspace# pip install pandas anacondaを使いたい、scikit-learnを使いたいなどの環境を選びたい場合 上のDockerfileのImageを変えれば良いです。 あるいは RUN でエラーにならないなら、pip installでも可能です。 python 3.9.7を使いたい場合 Dockerfile FROM python:3.9.7 RUN python -m pip install -U flake8 autopep8 ipykernel python 3.6.15 + alpineを使いたい場合 Dockerfile FROM python:3.9.7 RUN python -m pip install -U flake8 autopep8 ipykernel tensorflow, pytorchを m1 macbook ホストで使いたい場合 Dockerfile FROM sonoisa/deep-learning-coding:pytorch1.6.0_tensorflow2.3.0 RUN python -m pip install -U flake8 autopep8 ipykernel こちらのimageは以下の記事を参考に使わせていただいてます。 M1搭載Macでも環境を汚さずにDeep Learningしたい! - Qiita その他、こういう使い方をしたいなどがあればコメントしてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python/networkx】にじさんじライバー間のコラボ関係のネットワーク分析

はじめに この1ヵ月程度,大学院での研究のために複雑ネットワークの勉強しており,実際のデータを用いてネットワーク分析で遊んでみたいなと思いました. どのようなデータを用いてネットワーク分析を行おうか考えていたとき@fufufukakakaさんの記事を見つけ,自分がにじさんじのライバーさんの配信をよく視聴することもあり,同じようなネットワーク分析がしてみたいなと思い立ちました. しかし,元の記事のような高度なスクレイピングを応用するには自身の技術が足りないので,今回はもっと簡単に,にじさんじ非公式wikiから有志の方によってまとめられたにじさんじライバー間のコラボ一覧表をお借りして,にじさんじライバーのコラボ関係ネットワークを作成しそれを分析してみることにしました. 目標 テーブルデータのスレイピング・処理の練習 ネットワーク分析(Python/networkx)の使い方を学ぶ ネットワーク分析の観点から,ライバー間のコラボ関係ネットワークについて,どのようなことが言えるのか考える 実装 今回の実装は以下の4つの手順で実装していきます. 必要なパッケージのインポート にじさんじ非公式wikiからコラボ一覧表をスクレイピング ライバー間のコラボ関係のネットワークを作成 ネットワーク分析 1. 必要なパッケージのインポート まず必要なパッケージをインポートしていきます # パッケージのインポート import requests from bs4 import BeautifulSoup import itertools import networkx as nx 今回使ったpythonとパッケージのバージョンは以下の通りです python == 3.8.8 requests == 2.25.1 beautifulsoup4 == 4.9.3 2. にじさんじ非公式wikiからコラボ一覧表をスクレイピング 今回,ネットワーク作成に使わせていただくコラボ一覧表はテーブルデータでまとめられているので,スクレイピングしてデータを取ってきます.今回スクレイピングを行うために使ったコードはこの本で解説されていたコードを参考にしました(というかほぼそのまま使ってます). # URL(にじさんじ非公式wiki) url = "https://wikiwiki.jp/nijisanji/コラボ一覧表" # URLからページ内容を取得 r = requests.get(url) html_contents = r.text # HTML構文解析 html_soup = BeautifulSoup(html_contents) # コラボ一覧表のテーブルを抽出 combi_table = html_soup.find("div", class_="wikiwiki-tablesorter-wrapper").find("table") # テーブルの項目名の抽出 headers = [] for header in combi_table.find("tr").find_all("th"): headers.append(header.text) # 最初の行(項目名)以降の行に対して処理(各コラボの 人数/コラボ名/詳細の有無/メンバー の抽出) values = [] for row in combi_table.find_all("tr")[1:]: temp_list = [] # 一時格納用リスト # th:コラボ名, td:それ以外の情報 for col in row.find_all(["th", "td"]): temp_list.append(col.text) values.append(temp_list) 上のコードを実行すると,コラボ一覧表をそのままスクレイピングできます.しかし,コラボのメンバーからネットワークを作成するには,データの処理が必要になります. 例えば,JK組(月ノ美兎・静凛・樋口楓(敬称略))の3人の場合,以下のように人名を数値に変換し,その数値の組み合わせを考える必要があります. 月ノ美兎 → 1, 静凛 → 2, 樋口楓 → 3 この場合考えられる数値の組み合わせは (1,2), (1,3), (2,3) 実際にnetworkxを用いて,高度に抽象化されたJK組を描いてみます. import networkx as nx import matplotlib.pyplot as plt G = nx.Graph() # ノード数:3 の無向ネットワークを作成 G.add_nodes_from([1, 2, 3]) # 月ノ美兎 -> 静凛 へのリンク G.add_edge(1, 2) # 月ノ美兎 -> 樋口楓 へのリンク G.add_edge(1, 3) # 静凛 -> 樋口楓 へのリンク G.add_edge(2, 3) # 可視化 nx.draw(G) plt.show() 今回は,先ほどスレイピングしたデータからコラボしているライバー名(にじさんじ外のVTuberの方も含む)で重複を避けるためにセットを作り,名前に順に数値を振っていき,コラボしている方同士の組み合わせを導出することで,どのライバー同士がコラボ経験があるのかを,上記のJK組の例のように2つの数値の組 (i,j) で表していきます. # 名前を数値に置換していく s = set() name_to_int = {} # コラボしたメンバーの重複なしのセットを作成 for i in range(len(values)): for name in values[i][3].split(", "): s.add(name) # 作成したセットから各メンバーの名前に番号を振る for name, i in zip(s, range(1, len(values)+1)): s_dict = {name:i} name_to_int.update(s_dict) # コラボしたメンバーに対応する数値の組を作っていく for i in range(len(values)): temp_list = [] combi = [] for name in values[i][3].split(", "): number = name_to_int[name] temp_list.append(number) for pair in itertools.combinations(temp_list, 2): combi.append(pair) values[i][3] = combi # 後でネットワークノードにラベルを付けるための辞書 int_to_name = {v:k for k, v in name_to_int.items()} 今回は,先ほどスレイピングしたデータからコラボしているライバー名(にじさんじ外のVTuberの方も含む)で重複を避けるためにセットを作り,名前に順に数値を振っていき,コラボしている方同士の組み合わせを導出することで,どのライバー同士がコラボ経験があるのかを,上記のJK組の例のように2つの数値の組 (i,j) で表していきます. # 名前を数値に置換していく s = set() name_to_int = {} # コラボしたメンバーの重複なしのセットを作成 for i in range(len(values)): for name in values[i][3].split(", "): s.add(name) # 作成したセットから各メンバーの名前に番号を振る for name, i in zip(s, range(1, len(values)+1)): s_dict = {name:i} name_to_int.update(s_dict) # コラボしたメンバーに対応する数値の組を作っていく for i in range(len(values)): temp_list = [] combi = [] for name in values[i][3].split(", "): number = name_to_int[name] temp_list.append(number) for pair in itertools.combinations(temp_list, 2): combi.append(pair) values[i][3] = combi # 後でネットワークノードにラベルを付けるための辞書 int_to_name = {v:k for k, v in name_to_int.items()} 3. ライバー間のコラボ関係のネットワークを作成 手順2.までで,ネットワークの作成に必要なデータは揃ったので,ここからは実際にコラボ関係のネットワークを作成していきます. 今回は簡単のために,コラボの回数に関わらず,(コラボ一覧表に乗っている限りで)一度でもコラボしたことがあるライバー同士をリンクでつなぐようなネットワークを作っていきます.スクレイピングしたままのデータをそのまま使っているのでにじさんじ外のVTuberの方やYouTuberの方も含まれています. # ネットワークグラフの作成 G = nx.Graph() G.add_nodes_from(list(range(1, len(s)+1))) for i in range(len(values)): for edge in values[i][3]: G.add_edge(edge[0], edge[1]) # 各ノードにラベルを付与 H = nx.relabel_nodes(G, int_to_name) # Gephi用にGraphML形式に変換 nx.write_graphml_lxml(H, "nijisanji_combi.graphml") これでネットワークを作成することができました.ネットワーク分析用のソフトのGephiを用いて見やすく可視化していきます. コラボしたことがある人数が多いほどノードが赤く大きくなり,名前が大きく表示されるように調整をしています.この画像だけでも,色々と興味深いことがわかります. 4. ネットワーク分析 ここからは作成したネットワークから情報を抽出していきます.ただし,自分がまだ複雑ネットワークの知識にまだまだ乏しく,すべての指標を理解できるわけではないので,解釈が簡単なネットワークの指標のいくつかを用いて,コラボ関係のネットワークから得られる情報を考えていきます. 4.1 平均次数 平均次数は,1つのノードに対して張られているリンクの平均を表します.今回の場合,平均次数は「ライバーがコラボしたことのある平均人数」を表します. Deg_list = [d for n, d in H.degree()] ave_deg = sum(Deg_list)/len(Deg_list) print(ave_deg) 結果: 26.778846153846153 なので,平均26-7人とコラボしたことがあるということがわかります. 簡単ですが,次数のヒストグラムも描いてみます. plt.hist(Deg_list) plt.show() ヒストグラムでは,コラボしたことがある人数が10人以下程度の人が多いという結果が出ていますが,これは今回ネットワーク分析に使用したデータに,にじさんじ外のVTuber・YouTuberの方が入ってしまっていることが原因だと考えられます.それを考慮すると,次数の中央値は60あたりにありそうです. 4.2 クラスター係数 クラスター係数は,(誤解を恐れず)簡単に言ってしまえば「友達の友達が自分の友達である度合」を表す指標です.今回の場合,「どれくらい広くコラボが行われているか」ということを示す指標の1つになります. nx.average_clustering(H) 結果: 0.7149598722607242 あまり比較にはなりませんが,現実に存在するネットワークのクラスター係数は0.1から0.7程度らしいです(wikipedia:複雑ネットワーク). このことからにじさんじのコラボ関係ネットワークのクラスター係数はそこそこ高く,コラボ時のメンバーに偏りが少なくに広くコラボがやられてきたということがわかります. このあたりは「にじさんじ甲子園」や「にじさんじマリカ杯」,最近では「CRカップ」や「Ark」といった大規模なコラボ企画や,ライバーの方が同時にプレイしやすいゲーム企画が多いのでクラスター係数が高いというのは結構しっくりきます. 4.3 中心性 ここからはネットワーク全体ではなく,個々のノードに着目していきます.個々のノードに対して「中心性」を導出することで,各ノードがネットワーク全体にとって"要"であるかどうかを判断できます. 4.3.1 次数中心性 次数中心性は,各ノードに張られているリンクの数でノードの中心性を測るための指標です. 今回の場合は,「今までコラボした人数が多いほどネットワーク全体にとって重要である」というわかりやすい指標になってます.もっと単純に言ってしまえば「コラボ人数ランキング」ということになります. # 次数中心性(どれほど多くの人とコラボしているかを測る指標) deg_C = nx.degree_centrality(H) # 次数中心性が大きい順にソート deg_C_list = sorted(deg_C.items(), key=lambda x:x[1], reverse=True) print(deg_C_list) 結果: [('椎名唯華', 0.46376811594202894), ('でびでび・でびる', 0.43478260869565216), ('叶', 0.4154589371980676), ('鷹宮リオン', 0.4106280193236715), ('渋谷ハジメ', 0.4106280193236715), ('桜凛月', 0.3864734299516908), ('緑仙', 0.3864734299516908), ('花畑チャイカ', 0.37681159420289856), ('伏見ガク', 0.37681159420289856), ('夜見れな', 0.3719806763285024), ... 4.3.2 近接中心性 次数中心性は,各ノードが他のノードまで平均的にどれくらい近いかを測るための指標です. 今回の場合は,「コラボしたことがない人も含め,他の人との平均的な距離」という指標になっています.具体的な式は,あるノード $i$ から他のノードへの平均距離を $L_{i}$ として,近接中心性 $C_{i}$は $$ C_{i} = \frac{1}{L_{i}} $$ で表されます. # 近接中心性(自分から他人への距離がどれだけ近いかを測る指標) clo_C = nx.closeness_centrality(H) clo_C_list = sorted(clo_C.items(), key=lambda x:x[1], reverse=True) clo_C_list 結果: [('でびでび・でびる', 0.5001890359168241), ('椎名唯華', 0.49874340286504143), ('アルス・アルマル', 0.49587706146926536), ('渋谷ハジメ', 0.48747236551215917), ('桜凛月', 0.4833759590792839), ('叶', 0.4833759590792839), ('葛葉', 0.4753862738052462), ('剣持刀也', 0.47278141751042285), ('花畑チャイカ', 0.47278141751042285), ('夜見れな', 0.47148966500356376), ... 4.3.3 媒介中心性 次数中心性は,各ノードがネットワークの流れを制御する度合いを測る指標です. 今回の場合は「コラボしたことがないライバー同士の橋渡し役としての重要性」を示す指標になっています. # 媒介中心性("橋渡し役"としてどれぐらい重要かを測る指標) bet_C = nx.betweenness_centrality(H) bet_C_list = sorted(bet_C.items(), key=lambda x:x[1], reverse=True) bet_C_list 結果: [('ヌン・ボラ', 0.09566308995092591), ('小野町春香', 0.08902051296783826), ('明楽レイ', 0.06554213546387946), ('ソ・ナギ', 0.061846325534069095), ('イ・ロハ', 0.05134859774139868), ('西園チグサ', 0.0476863865691726), ('椎名唯華', 0.045909796752497574), ('周央サンゴ', 0.03929087934520966), ('ラトナ・プティ', 0.03516799735096999), ('Miyu Ottavia', 0.034613761080624736), ... 4.3.4 固有ベクトル中心性 固有ベクトル中心性は,中心的なノードにつながっているノードも中心が大きという考え方のもと,各ノードの中心性を測る指標です. # 固有ベクトル中心性(自分がどれだけ"中心"に近いかを測る指標) eig_C = nx.eigenvector_centrality(H) eig_C_list = sorted(eig_C.items(), key=lambda x:x[1], reverse=True) eig_C_list 結果: [('でびでび・でびる', 0.1477684327754912), ('椎名唯華', 0.14720982804405108), ('鷹宮リオン', 0.14457071842682492), ('渋谷ハジメ', 0.14363375546129273), ('桜凛月', 0.1419820816810376), ('夜見れな', 0.14094732979420704), ('緑仙', 0.1400844559603873), ('叶', 0.1400261470640506), ('アルス・アルマル', 0.13813952769222942), ('不破湊', 0.13720967966477118), ... 5. 中心性の観点からのコラボ関係ネットワークへの簡単な考察 各種中心性から,今回のコラボ関係ネットワークでは「でびでび・でびる」さんと「椎名唯華」さんの二人が,コラボ関係ネットワークであるという結果が出ました.次数中心性・近接中心性・固有ベクトル中心性の3つの中心性で,お二人ともトップに入っており,他のトップ10の方たちがある程度入れ替わっているところから,今回のネットワークで中心的であるのはこのお二人ということで間違いはないのかなと思います. また,媒介中心性では,「ヌン・ボラ」さんや「明楽レイ」さんをはじめとした「にじさんじKR」の方が多いことが特徴として出てきています.最近では,FPSゲーム「Apex Legends」でのコラボ配信や大会などで,関わることが多くなってきているので,「にじさんじKR」の方が全体的に媒介中心性が高くなっているのだと思います.さきほどのネットワーク図を見る限り,国境を越えてのコラボはまだまだ少ないなという印象があり,それが媒介中心性にも表れています. ただし,重要な注意点として,今回のコラボネットワークでは「固有名称が存在するコラボ」しか扱っていません.実際には,今回のネットワーク以上にコラボが行われていることは,考察を深めていくためには考慮しなければならない要素になっていきます. 6. 総括 今回はにじさんじ非公式wikiからスクレイピングしたにじさんじライバーのコラボ関係から作成したネットワークを分析し,どのような特徴を持つのかについて調べました. このネットワーク図は,とりあえずのデータを突っ込んで作成したものですが,直感的にはわからないこと(誰が一番コラボしているのか?,コラボ関係は全体としてどんな形なのか?)を定量的に評価できる点が非常に良い点であると考えてます.応用可能性としては, あるライバーの配信の視聴者に,類似した内容の配信を行うライバーを推薦するシステム ネットワークを構築する範囲を広げることで,Vtuber界隈の全体像を把握する 等が挙げられます.ただし,改良点も多く 固有名称のあるコラボしか考慮に入れていない コラボ回数を考慮していない 等が大きな問題として挙げられます.後者の問題は,「コラボ回数が多いほど結びつきが強くなる」ような「重み付きネットワーク」を導入することで,より深い考察ができるようになりますが,データがまとめられておらず,ネットワークを作成するにはすこし難しいかもしれません. おわりに 今回初めて記事を書いたので,至らぬところが多々あると思います.記事の中に誤植や,用語についての誤り,質問等があればコメントを頂けると助かります. また,私事にはなりますが,推しているライバーさんのチャンネルへのリンクを張ることで,記事の締めとさせていただきます. 加賀美ハヤト さん : https://www.youtube.com/channel/UCmovZ2th3Sqpd00F5RdeigQ ジョー・力一 さん : https://www.youtube.com/channel/UChUJbHiTVeGrSkTdBzVfNCQ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む