20210916のPythonに関する記事は17件です。

Python分析に便利なDockerfile

初めに  この記事は、自分用に作成したPython分析環境を共有するために書いています。Pythonで分析する際は、テーブルデータや画像、自然言語等が主な対象データになると思いますが、今回作成したDockerfileは、Conda、OpenCV、MeCabをインストールする構成にしたので、色々な分析のBaselineにすることが可能です。(自然言語処理をしない場合はMeCab部分のコードを消す等してください。)  次の章から、作成した環境について説明をしていきますが、Docker等の単語の意味をここでは説明しません。Qiita には良質な解説記事が多くありますので、基本的な単語については、そちらを参考にしてください。 環境  検証した環境を以下に示します。仮想環境には WSL2 ではなく、VirtualBox を使いました。WSL2 は別のマシンで環境構築した際に苦戦した思い出があるので、今回は VirtualBox + PowerShell で実施しています。 ・Windows 10 Pro 64bit ・Docker Desktop 4.0.0 (67817) (Download page) ・Docker Engine 20.10.8 ・Docker-compose v2.0.0-rc.2 Docker Desktop for Windows を使っていますが、場合によっては有料となっているため、企業が利用する場合等は注意ください。以下は ITmedia の記事です。 Docker Desktopが有料化へ 従業員数250人未満・年間売り上げ1000万ドル未満の組織などは引き続き無料 Docker インストールはこのあたりを参照ください。インストーラーが少し重いのでダウンロードに多少時間がかかります。(私の場合20分程度) WindowsでDocker環境を試してみる フォルダ構成  Buildする際のフォルダ構成を以下に示します。3つのフォルダ(input / output / notebook)は、分析の際に .ipynbファイルやデータを置く場所です。Image内にこれらのフォルダを含める必要がない(Build の際には使わない)ため、.dockerignore に3つのフォルダ名を記載しています。また、docker-compose.yml に書いてある通り、このフォルダはマウントされるため、Build 完了後に起動する Jupyterlab からこのフォルダが見えるようになります。そのため、Windows とのやり取りはここで行うことが可能です。 Container name/ (ここは何でもOK)  ┠ input/  ┠ output/  ┠ notebook/  ┠ .dockerignore  ┠ clean-layer.sh  ┠ docker-compose.yml  ┠ Dockerfile  ┠ requirements_conda.txt  ┠ requirements_pip.txt  これら一式を 私のGithub に Upload したので、欲しい人はダウンロードしてご活用ください。 各ファイルの説明  ここから各ファイルの内容について、要点を絞って解説していきます。  まずは、docker-compose.yml からです。これは、以下のサイトを参考に作成しました。フォルダ構成等も大変参考になりました。 Dockerでデータ分析環境を手軽に作る方法  やっていることを簡単に言うと、Image を Build して、最後の行に書かれている command を実行しています。この形で書いておくことによって、毎回長い docker run のコマンドをたたく必要がなく、覚えやすいコマンドで実行が出来るようになります。 docker-compose.yml version: "2.3" services: jupyter: build: . volumes: - .:/tmp/working working_dir: /tmp/working ports: - 8888:8888 command: jupyter lab --ip=0.0.0.0 --allow-root --no-browser ・build: .:実行した場所にあるDockerfileを実行します。 ・volumes: .:/tmp/working:実行した場所をマウントします。 ・ports: 8888:8888:Localhost:8888 をDocker のポート8888 に転送します。さらに、Jupyter側がポート8888(デフォルト)で待っているので、Localhost:8888 にアクセスすることで、Jupyter に繋がるようになります。  GPUを使いたい場合は、build と同じ階層に runtime: nvidia を追加します。ここの書き方は docker-compose.yml の冒頭に記載している version に左右されることに注意ください。  次は、clean-layer.sh です。これは、Kaggle公式のGithub と同じものです。Docker では、RUN コマンド等を実行する度に Layer が作成され、RUN を書けば書くほど最終的に出来上がる Image が重くなります。そのため、多くの人は1つの RUN コマンドに多くのコマンド(apt-get/pip等)を書いていると思います。しかし、RUN の最後にゴミを削除するコマンド(clean-layer.sh)を実行すれば、最終的な Image が重くなりません(どこまでかは未検証ですが・・・)。RUN を複数に分けることは、可読性向上に繋がるため、この記事では clean-layer.sh を挟む実装を採用しています。 clean-layer.sh #!/bin/bash # # This scripts should be called at the end of each RUN command # in the Dockerfiles. # # Each RUN command creates a new layer that is stored separately. # At the end of each command, we should ensure we clean up downloaded # archives and source files used to produce binary to reduce the size # of the layer. set -e set -x # Delete files that pip caches when installing a package. rm -rf /root/.cache/pip/* # Delete old downloaded archive files apt-get autoremove -y # Delete downloaded archive files apt-get clean # Ensures the current working directory won't be deleted cd /usr/local/src/ # Delete source files used for building binaries rm -rf /usr/local/src/* # Delete conda downloaded tarballs conda clean -y --tarballs  最後に Dockerfile の解説を行います。こちらの書きっぷりも大変 Kaggle の Dockerfile が参考になりました。基本的には、Ubuntu20.04 + MiniConda + JupyterLab の環境であり、そこに Python のライブラリを入れているイメージです。 Dockerfile FROM ubuntu:20.04 ADD clean-layer.sh /tmp/clean-layer.sh COPY requirements_conda.txt ./ COPY requirements_pip.txt ./ ARG DEBIAN_FRONTEND=noninteractive # Make sure python version you want to use ENV PYTHON_MAJOR_VERSION=3 ENV PYTHON_MINOR_VERSION=7 ENV PATH="/root/miniconda3/bin:${PATH}" # Update and install necessary modules. RUN apt-get update && \ apt-get install -y git vim wget zip unzip curl make cmake && \ wget --quiet https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \ mkdir /root/.conda && \ bash Miniconda3-latest-Linux-x86_64.sh -b && \ rm -f Miniconda3-latest-Linux-x86_64.sh && \ chmod +x /tmp/clean-layer.sh && \ /tmp/clean-layer.sh # Install python RUN conda config --add channels conda-forge && \ conda config --add channels nvidia && \ conda config --add channels pytorch && \ conda config --add channels rapidsai && \ conda install --yes python=$PYTHON_MAJOR_VERSION.$PYTHON_MINOR_VERSION && \ apt-get install -y python3-pip && \ /tmp/clean-layer.sh # Install conda packages. # Conda can solve dependency problem, and running speed is fast more than pip packages. RUN conda install --yes --file requirements_conda.txt && \ /tmp/clean-layer.sh # Install pip packages. The packages, libsm6 libxrender1 are needed for opencv. RUN apt-get install -y libsm6 libxrender1 --fix-missing && \ pip3 install --no-cache-dir -r requirements_pip.txt && \ /tmp/clean-layer.sh # Install MeCab and NEologd dic (Japanese useful dictionary) RUN apt-get install -y sudo mecab mecab-ipadic libmecab-dev && \ git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git && \ chmod 777 ./mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd && \ ./mecab-ipadic-neologd/bin/install-mecab-ipadic-neologd -y -n && \ cp /etc/mecabrc /usr/local/etc/mecabrc && \ /tmp/clean-layer.sh # Install jupyterlab and jupyter-kite (https://github.com/kiteco/jupyterlab-kite) RUN curl -sL https://deb.nodesource.com/setup_12.x |bash - && \ apt-get install -y --no-install-recommends nodejs && \ pip3 install --upgrade --no-cache-dir 'jupyterlab~=3.0' jupyterlab-git && \ wget --quiet https://linux.kite.com/dls/linux/current && \ pip3 install --upgrade --no-cache-dir jupyter-kite && \ jupyter labextension install "@kiteco/jupyterlab-kite" && \ /tmp/clean-layer.sh  ベースイメージは Ubuntu20.04 にしていますが、GPU を使う場合は、ベースイメージを以下等に変更してください。 From nvidia/cuda:11.0.3-cudnn8-devel-ubuntu18.04 ・ARG DEBIAN_FRONTEND=noninteractive:cmake をインストールした際に、タイムゾーン選択が出て、Build が止まってしまうので、interactive dialogue が出ないようにしています。https://askubuntu.com/questions/909277/avoiding-user-interaction-with-tzdata-when-installing-certbot-in-a-docker-contai ・NEologd dic:正式名称は「mecab-ipadic-NEologd」。結構新しい単語もはいっている辞書で有用。https://github.com/neologd/mecab-ipadic-neologd ・jupyter-kite:Jupyter で Autocomplete を使えるようにします。https://github.com/kiteco/jupyterlab-kite Build 結果 コマンドは以下です。 docker-compose up --build または、 docker-compose build --no-cache docker-compose up  --no-cache は、以下のエラーが Dockerfile 作成の試行錯誤中に何度か出たので、dockerでmysqlが立ち上がらなくなった。 を参考に追加しました。もし、ご自身で試行錯誤を実施した際にこのエラーが発生したら試してください。 failed to solve: rpc error: code = Unknown desc = failed to solve ...  docker-compose up を実行すると Jupyter lab にアクセスする URL (http://127.0.0.1:8888/lab?token=...) が表示されるので、アクセスしてください。以下の画面が表示されます。 Git 連携  今回、jupyterlab-git もインストールしているため、GitHub / GitLab との連携が可能です。試しに 私のGithub から clone してみます。まずは、clone するフォルダを配置する場所に移動します。今回は input の下に移動します。そして、以下画像の赤枠をクリックします。  次に、Clone a Repository をクリックし、Clone したい Repository の URL (https://github.com/tt20171105/Machine-Learning 等)をコピペします。最後に CLONE をクリックすることで、input 直下に Machine-Learning フォルダが作成されます。これでご自身の Git を連携して修正して push したり、他の Repository を Clone してきて使ったりと便利になると思います。是非活用ください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python Django】マイグレーション(DB)をロールバック、元に戻す

Djangoはマイグレーションファイルからテーブルの定義変更や追加する機能がDjango ORマッパーにあります。さらに「ロールバック」つまり、マイグレーションファイルをもとにデータベースを前の定義に戻すこともできます。 用途としては、 チームで開発してて、migrateした後に「こっちに修正した方が良くない?」と言われたので修正必要 migrateしたけど、定義抜けてたからロールバック(戻して)定義追加し直したい などに使えます。 ある程度Djangoで開発していくなら必須の知識ですね! DjangoのDB・モデルの基礎は以下のように書いてますので良ければ 今回以下の記事のmodel定義を使用して記事作成しました。 マイグレーション履歴を確認 python manage.py showmigrations マイグレーション履歴を確認できる。 どこに戻せばいいか、「アプリ名」、「マイグレーション名」を確認。 accounts [X] 0001_initial [X] 0002_order_product_tag admin [X] 0001_initial [X] 0002_logentry_remove_auto_add [X] 0003_logentry_add_action_flag_choices auth [X] 0001_initial [X] 0002_alter_permission_name_max_length [X] 0003_alter_user_email_max_length [X] 0004_alter_user_username_opts [X] 0005_alter_user_last_login_null [X] 0006_require_contenttypes_0002 [X] 0007_alter_validators_add_error_messages [X] 0008_alter_user_username_max_length [X] 0009_alter_user_last_name_max_length [X] 0010_alter_group_name_max_length [X] 0011_update_proxy_permissions [X] 0012_alter_user_first_name_max_length contenttypes [X] 0001_initial [X] 0002_remove_content_type_name sessions [X] 0001_initial 今回はaccountsアプリの0002_order_product_tagをロールバックにして元に戻すことにします。 ロールバックのコマンドはmigrate rollbackみたいなコマンドはなくて、migrateコマンドにどこまで戻るかをマイグレーション名を入力することで行うことができます。 書き方は python manage.py migrate <アプリ名> <マイグレーション名> 例: python manage.py migrate accounts 0002_order_product_tag で指定したマイグレーションまで戻る マイグレーションファイルの修正や削除 先ほどのコマンドでロールバックすることができました。変更をなかったことにするなら、該当のマイグレーションファイルを削除すれば完了です。 もし定義を変更するなら パターン1: modelを編集してmakemigrations できればこれやった方が安全。 models.pyを編集してマイグレーションファイルを削除 今回は私の場合はaccounts/migrations/0002_order_product_tag.pyを削除ですね。 python manage.py makemigrations でもう一度作成、 python manage.py migrate これで完了。 パターン2: マイグレーションファイルを直接編集 知識ある場合に使ってください。 マイグレーションした時に、新規データを作成したり、既存のデータを変更・削除したいなどなど、特別な処理を記述する場合は該当ファイルを編集してください。 あとは、同じく python manage.py makemigrations でもう一度作成、 python manage.py migrate 全てを初期の状態に戻したい たまに、まっさらな状態に戻したいということもあると思います。そんな場合は以下のコマンドを使用。 python manage.py migrate <アプリ名> zero 例: python manage.py migrate apps zero
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cartopyでurllib.error.URLErrorが出た時の対処

私,初心者です.内容にはご注意.また,異なる理由で同じエラーの出る場合もあるかもしれないので,ひとつのケースとして参考までに. バージョン 確認できたものだけ(真面目に全バージョン検証したわけでない) cartopy 0.19.0.post1 解決 ... /miniconda3/lib/python3.9/site-packages/cartopy/io/shapereader.py ないの300行くらいのところにある_NE_URL_TEMPLATEのアドレスを書き換える 修正前 _NE_URL_TEMPLATE = ('https://naciscdn.org/naturalearth/{resolution}' '/{category}/ne_{resolution}_{name}.zip') 修正後 _NE_URL_TEMPLATE = ('https://naturalearth.s3.amazonaws.com/' '{resolution}_{category}/ne_{resolution}_{name}.zip') 今回はcartopy.fearure.BORDERSをax.add_featureで実行した. 初めて実行するタイミングでデータのダウンロードが発生し,上記のURLが参照されているよう.故にそのタイミングが肝と思われ,少し前(2021年の2月とか)はnaciscdn.orgにアクセスできたが最近(2021年9月)はアクセスできなくなっている. amazonのサーバーを指定すると大丈夫のよう.最新版のcartopyでcaciscdnを指定しているものもあったので,そもそもcondaのバージョンも関係している可能性あるが,検証はしていません. 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Classiのメッセージを日付で絞り込む

Classiは学校向けのSNS(?)で先生からの連絡、課題提出等さまざまなことができるアプリです。 とても便利なアプリですが、情報量が多くなってしまうという欠点があります。 今回は情報量を絞り込むために、メッセージを絞り込める様にしました。 使用したもの ・jupyter lab ・python 注意:初心者が作ったものなのでいわゆるクソコードになっております。ご了承ください。 auto_classi.ipynb !pip install selenium !brew install chromedriver from selenium import webdriver from time import sleep # 関数 def spliter(): #メッセージごとに境界線を書く print("\n"+ "-----------------------------------------------------------------------------------------------------------------") # chrome起動 browser = webdriver.Chrome() # classiを開く url = 'https://auth.classi.jp/students' browser.get(url) # classiログイン element_username=browser.find_element_by_id("classi_id") element_username.send_keys("hogehogehoge") #<--ご自身のIDを入力 element_password=browser.find_element_by_id("password")  element_password.send_keys("hogahogahoga") #<-- ご自身のパスワードを入力 element_login_btn=browser.find_element_by_class_name("pen-mod-btn") element_login_btn.click() # 校内グループを開く sleep(1) group_btn=browser.find_element_by_class_name("btn11") group_btn.click() # メッセージの詳細を開く sleep(3) detail_btns=browser.find_elements_by_class_name("see-next") for index in range(len(detail_btns)): detail_btns[index].click() # メッセージの内容を取得 sleep(1) messages=browser.find_elements_by_class_name("mod-message") date="2021/09/11" #<--必要に応じて日付を変えてください 。注意: 西暦/月/日(八月-->08 とする) for index in range(len(messages)): if date in messages[index].text: spliter() print(messages[index].text+"\n") browser.quit() #<--ブラウザを閉じる よかったらお使いください
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「自動化ツール」活用は、これからのデータ分析スキル向上の鍵だ!(と思う)

2021/09/17:(試行)5回目の結果とデシジョンツリー分析を追加 ~データを放り込むだけで、データ前加工 ⇒ 学習 ⇒ モデル選択 ⇒ モデル構築 ⇒ 予測 までほぼ自動って・・・どうなってんの?!~   はじめに 機械学習自動化ライブラリ「PyCaret」は、未来にワープしたかのような衝撃があります。 ご存じない方のために、 できるだけ専門用語はさけ、私なりに魅力をお伝えしつつ、あるデータセットでPyCaretを適用、奮闘した記録を報告させていただきたいと思います。 PyCaretってなに?   PyCaretはPythonライブラリのひとつです。 ホームページでは以下のように謳われています。 ”PyCaret is an open source, low-code machine learning library in Python that allows you to go from preparing your data to deploying your model within minutes in your choice of notebook environment.PyCaret is an open source, low-code machine learning library” ざっくり訳すと、 Pycaretは、Pythonで使えるオープンソースの機械学習ライブラリ、わずかなコードでデータ準備からモデル展開までが数分で! という感じです。 また、『無料のAutoMLライブラリ』といったタイトルもよく目にします。 「AutoML」は、日本語で「機械学習の自動化」のことです。 ここまでの説明で、 ”機械学習”なんて、私には関係ないから。。。 とスルーしてしまうと、将来とても後悔することになると思います。 「AutoML」の出現により、データ分析のありかたは、今後変わるかもしれないからです。     これまでは、 手法を知っている人が武器(ツール・アプリ)を持っていないと実施できなかったデータ分析が、AutoMLにより、知らない人でも簡単に実行できます。 例えば、 データが「男/女」「晴/曇/雨」等と区分されている場合、「0/1」等、データ分析できる変数に勝手に変えてくれるのです。 データが偏った分布であれば、正規分布に近づけるような処理も勝手にしてくれるのです。 「どの手法を使うか?」について迷う必要はないのです。「この場合に使える手法はこれだけあります。今回はこれが一番いいですよ。」と教えてくるのです。 予測のためのモデルも、手元データだけに過剰にフィットしたものにならないよう検証を行った上で、ギリギリまで追い込んでもくれるです。 これは革命といってよいと思います。   一方、データを読む力は "ますます" 大事にもなる 先に紹介したAutoMLでできることをみれば、ほぼ手放しに近いように思えますが、どの様なデータを与えるかは、(これまで通り)よくよく考えないといけません。 これは、人に対しても機械に対しても同じだと思います。 「これはどんなデータ?」 と問われたとき、関係する変数をいっぱい挙げるだけでは、聞き手の方は「聞けば聞くほどわからない」ということになります。 挙げられた変数は意味のある変数もあるだろうけど、意味のない変数もあるかもしれない。。。 要は、結果に関係するであろう変数の特徴が知りたい。ただ、これは言うほど簡単ではありません。 例えば、「コンビニのアイスクリームの売行きを予測したい」という時、どのような特徴量が必要となるでしょうか? 「夏場に気温が上がると駅近くのコンビニに足が向く」でしょうから、季節、気温等もデータに取り込んだ方がよいでしょうし、気温と湿度から「猛暑日か否か?」とか、店舗の位置(住所)から「駅近くか否か?」等、手元にある変数同士をつなぎ合わせて新たな特徴を設定したり、結果に影響するであろう変数を新たに設けたり、より特徴ある変数に変身させたり・・・これらを『特徴量エンジニアリング』と呼ぶそうです。 これは容易くはないですが、AutoMLは とにかく簡単ではやいので、何度も試行でき、試行を繰り返す中で得た知見を『特徴量エンジニアリング』に反映し、また試行するということが現実的にできます。 このような試行錯誤は『特徴量エンジニアリング』のスキル向上にもつながりますので、AutoMLの活用は、まちがいなく生産性向上に直結するということになります。 PyCaretでデータセット「Bike Sharing Demand」に挑んでみる 実行条件など ・Google colabで実行 ・Kaggleの「Bike Sharing Demand」で実行 データセット「Bike Sharing Demand」について PyCaret適用に挑んだ データセット「Bike Sharing Demand」は、 会員登録、レンタル、返却のプロセスを自動化した自転車シェアリングシステムのデータで、ワシントンD.C.における自転車レンタルの需要を過去の利用パターンと気象データを組み合わせて予測するというコンペで使われたものです。 データセット「Bike Sharing Demand」の項目と内容 項目 内容 datetime 毎時の日付 + タイムスタンプ season 1 = 春、2 = 夏、3 = 秋、4 = 冬 holiday その日が休日であるかどうか workingday その日が週末でも休日でもないかどうか weather 1: 晴れ/少ない雲/部分的に曇/一部曇り、2: 霧+曇り/霧+崩れた雲/霧+少ない雲/霧、3:小雪/小雨+雷雨+散らばる雲/小雨+散らばる雲、4:大雨+氷片+雷雨+霧/雪+霧 temp 摂氏温度 atemp 摂氏で表した「体感温度」 humidity 相対湿度 windspeed 風速 casual 未登録ユーザーによるレンタル数 registered 登録ユーザーによるレンタル数 count 総レンタル数 いきなりミス! 最初にデータをプロファイリング(方法は以下参照)にかけ、ざっと眺めました。 最初の印象は、 「欠損しているデータはないし、カテゴリーデータ、例えば「season」という項目でいえば、1 = 春、2 = 夏、3 = 秋、4 = 冬といった形にラベルエンコーディングされてもいて、前処理的には行き届いている感じだなぁ。」   そこで、早速、データセットをPyCaretに投入しました。 「count(総レンタル数)」を目的変数(Target)として設定したくらいで、あとはほぼPyCaretお任せでした。 予測までの過程は超順調! しかし、いよいよ完了という手前、テストデータを受け付けてくれず、エラーで止まってしまいました。 トレイン(学習)データには、「casual(未登録ユーザーのレンタル数)」「registered(登録ユーザーのレンタル数)」という項目があります。 以下のように、総レンタル数は未登録ユーザーと登録ユーザーの和となっています。 count(総レンタル数)⁼ casual + registered いずれのレンタル数も結果ですから、トレイン(学習)データにある「casual」「registered」は、テストデータにはある訳がありません。 いきなり・・・ですが、選択項目を間違ったわけです。 早速、以下のように ignore_features で学習に取り上げない項目を指定しました。 取り上げない特徴量(ignore)を指定 from pycaret.regression import setup clf = setup(data=train_data, target="count", session_id=123, ignore_features = ['datetime','casual','registered']) 初回 設定見直しで無事にモデル評価まで完了、早速kaggleでSubmitしてみました。 評価は、RMSLE(対数平均二乗誤差)で行われ、(実は正確に覚えていないのだが)Scoreは1.4あたりでした。 このデータセットの(Submitの)「エントリー数」と「スコア(RMSLE)」の関係をグラフ化してみました。 ほぼ完全にAutoML任せで実施しただけと考えると、悪くはないと思います。 ※それにしても100回以上エントリーしている方がいることに驚きました。 2回目 このデータセットの特徴には、temp|摂氏温度|、atemp|体感温度|、humidity|相対湿度|があります。 「よーし、自転車を借りるぞ」という時、それぞれ影響はあるだろうが、これら合わせ技で感じる快適さ、不快さの方が影響を与えるのではないか?と思い、不快指数を特徴量として追加することにしました。 不快指数は、このサイトの計算式を使わせていただきました。 ただ、これはPython初心者の私にとっては大変・・・ なんとか、計算結果をデータフレームに反映できました。 不快指数を追加 train_data = train_data.assign(fukai=train_data['temp']*0.81 + train_data['humidity']*0.01*(train_data['temp']*0.99-14.3)+46.3) これにてモデル評価まで完了、早速kaggleでSubmitしてみました。 Scoreは1.362658と、すこしだけですが改善しました。 3回目 つぎに時系列でレンタル数の傾向変化を見てみました。 レンタル数は、冬場は少なく、春以降に増加という季節変動がありそうです。また2011年よりも2012年の方がレンタル数が全体的に増加していることもわかります。 データには「datetime」という項目があります。 datetimeは、”2011-01-01 04:00:00” のような形式のデータです。この場合「2011年1月1日 4:00」です。 このdatetimeから、年, 月, 時間(hour)・・・を取りだし、特徴量に加えてみることにしました。 じつはこの作業、 自らのスキルに屈し、Excelで行ったのですが、Excelで例えばこのデータセットの「train.csv」を開くと、datetimeの表示が化けてしまいました。 csv:”2011-01-01 04:00:00” ⇒ excel:”2011/1/1 4:00:00” これでは、submitできなくなりますのでPythonで対応する方法を調べることにしました。 Pythonに「datetime」という日付や時刻に対してさまざまな操作をすることができるモジュールがあることがわかりましたので、以下のように実行しました。 ※以下は、trainデータで実行したものです。testデータでも同じことをしています。 datetimeインポート import datetime from datetime import timedelta train_data["datetime"] = pd.to_datetime(train_data["datetime"]) train_dataの”datetime”から年,月,日…を取り出し、新たな列に配置 train_data["year"] = train_data["datetime"].dt.year train_data["month"] = train_data["datetime"].dt.month train_data["day"] = train_data["datetime"].dt.day train_data["dayofweek"] = train_data["datetime"].dt.dayofweek train_data["hour"] = train_data["datetime"].dt.hour train_data.head() これにてPyCaretを実行し、前処理の結果を見たところ、hourが数値として認識されていました。 数値なのですが、1~12までのカテゴリーデータなので、以下のように設定しました。 hourをカテゴリーデータに設定(categorical_features) from pycaret.regression import setup clf = setup(data=train_data, target='count', session_id=123, ignore_features = ['datetime','casual','registered'], categorical_features= ['hour']) PyCaretは、ほぼお任せで実行できますが、このようなチューニングが必要な場合もありますね。 これで、モデル評価まで完了、早速kaggleでSubmitしてみました。 しかし、 残念ながら、Scoreは1.39068と、すこし悪化しました。 う~ん、時期,時間に対する影響は少なからずあると思うのですが・・・ もっとカテゴリーに濃淡をつけた方がよいのかもしれないのかなぁ。 ※詳しい方がおられたら、ぜひ助言をお願いいたします。 4回目 3回目は、よい結果が得られませんでした。 時系列カテゴリーの特徴がより明確になるような処置を検討するのが筋なのかもしれませんが、3回目の結果は一度忘れ(^^;)、別のアプローチを行うことにしました。 先に述べましたが、count(総レンタル数)は以下の関係にあります。 count(総レンタル数)⁼ casual + registered 3回目までは、count(総レンタル数)を目的変数(target)とした丼ぶり?勘定的アプローチでしたが、本来はcasual(非登録ユーザーのレンタル数)と registered(登録ユーザーのレンタル数)をそれぞれ予測し、この和をcount(総レンタル数)の予測とした方がよいと考えました。 これは、setup()で以下のように設定するだけなので何とかなりました。 casualを予測するときは、casualをtargetに、registeredとcountはignoreに registerdを予測するときは、registeredをtargetに、casualとcountはignoreに もっとよい方法があるのかもしれませんが、 casual予測結果は「submission_bike1.csv」に、registerd予測結果は「submission_bike2.csv」に出力しましたので、 ① まずそれぞれをdf1,df2というデータフレームに格納 ② df2に「count」列を設け、そこに df1['casual'] と df2['registered'] の和を格納 ③ df2のregisteredの列はいらないので削除 としました。以下がこれを実行したコードです。 submitファイルの合算 df1=pd.read_csv('submission_bike1.csv') df2=pd.read_csv('submission_bike2.csv') df2['count'] = df1['casual'] + df2['registered'] df2.drop("registered", axis = 1, inplace = True) # 合計したsubmitファイル(csv)を保存 df2.to_csv("submission‗final.csv", index=False) 無事にこれにてモデル評価まで完了、早速kaggleでSubmitしてみました。 Scoreは1.30025 に改善することができました。 5回目 時系列変数を取り入れた3回目は、よい結果が得られませんでしたので、リベンジすることにしました。 データ傾向から言えるのは 2011よりも2012年の方がレンタル数が上がっている。 冬場はレンタル数が他の季節よりも少ない。 registered(登録ユーザー)は、朝(AM7-9),夕(17-19PM)のレンタル数が多い。 いわゆるワーキングタイム以外(AM0-6,20-24PM)はレンタル数が少ない。 あと、 データにはhoriday(祝日)という特徴量はあるが、weekend(週末)という特徴量はない。週末はレンタルされる方が増えるのではないか? 季節変化が見られたので、3回目では単純にmonth(月)を取り入れたが、すでにseason(季節)という特徴量がありますので、month(月)は取り上げず、year(年)を取り上げることにしました。 hour(時間)は、時間帯でレンタル数が多い・普通・少ないの3つに区分することにしました。※特徴量名はhot_hourとしました。 あと、土曜日,日曜日をweekend(週末)として設定することにしました。 なお、今回もcasual(非登録ユーザーのレンタル数)と registered(登録ユーザーのレンタル数)をそれぞれ予測し、この和をcount(総レンタル数)の予測としました。 datetimeというライブラリで「年」等を抽出 #import datetime from datetime import timedelta train_data["datetime"] = pd.to_datetime(train_data["datetime"]) train_data["year"] = train_data["datetime"].dt.year train_data["dayofweek"] = train_data["datetime"].dt.dayofweek train_data["hour"] = train_data["datetime"].dt.hour 土日をweekendに train_data['weekend'] = 0 train_data.loc[train_data["dayofweek"] > 5, 'weekend'] = 1 train_data.head() AM7~9,PM17~19を"1"/AM0-6,PM20-24を"3"/これ以外を"2"に  train_data['hot_hour'] = 2 train_data.loc[train_data["hour"] == 7, 'hot_hour'] = 1 train_data.loc[train_data["hour"] == 8, 'hot_hour'] = 1 train_data.loc[train_data["hour"] == 9, 'hot_hour'] = 1 train_data.loc[train_data["hour"] == 17, 'hot_hour'] = 1 train_data.loc[train_data["hour"] == 18, 'hot_hour'] = 1 train_data.loc[train_data["hour"] == 19, 'hot_hour'] = 1 train_data.loc[train_data["hour"] < 7, 'hot_hour'] = 3 train_data.loc[train_data["hour"] > 19, 'hot_hour'] = 3 train_data.head() ※細かく区分したとはいえ・・・もっとよい方法がある気がします。どなたか教えてください。 特徴量重要度にyear_2012とhot_hour_3が出てきました。 trainとtestの残差に大きな差はありませんので、過学習は心配しなくてよさそうです。 精度は以下のような感じ。 無事にこれにてモデル評価まで完了、kaggleでSubmitしてみました。 Scoreは0.99989 に改善することができました。 デシジョンツリー(決定木:dtreeviz)でも見てみる デシジョンツリーでも見てみました。 「もう、最初からみときゃよかった」と思いました。 以下は、registered(登録ユーザー)を目的変数としたデシジョンツリーです。 まず、hot‗hourで分岐しています。1:出社・退社時間,2:日中,3:夜中~早朝 です。 夜中~早朝除いては、レンタル数が多いのは、冬(⁼season'1')を除いたシーズン、出社・退社の時間帯が多いことがわかります。 つぎに以下はcasual(非登録ユーザー)を目的としたデシジョンツリーです。 まず、atemp(体感温度)が高いか低いかで分岐しています。 atempが低い場合は、夜中~早朝を除き、レンタル利用者が多いことがわかります。 また、なんと!レンタル数はfukai(不快指数)は高いほど多くなっています。自転車に乗ることによって不快さを解消したいのかもしれません。 一方、気温による差はあまりなさそうですが、体感温度が高い場合は、レンタル数が下がっています。休日の日中に利用されることが多く、平日の利用者は休日の約半分となっています。 デシジョンツリーはいいですね。 registered(登録ユーザー)とcasual(非登録ユーザー)では利用シーンがぜんぜん違うということがよくわかりますし、これならばregistered(登録ユーザー)とcasual(非登録ユーザー)は別々に予測すべきと直感できます。   参考:デシジョンツリーに関してはこちらも見てください それにしても、dtreevizで描けるデシジョンツリーは美しいなぁ。 最後に 前処理で必要となる処理がもっとできるようにならないといけないな、特徴量エンジニアリングのスキルもより向上させないといけないなということはあるが、とにかく試行を何度も繰り返せることがPycaretの最大の魅力であると思いました。 私がPythonをもっと扱うことができればもっとスムーズだったともいますが、それでも半日程度で4回の試行ができました。 Pycaretがなければ、こんなにサクサクは試行できなかったですし、試行を重ねるにつれ、データに対する理解も深まってきました。 この記事は、精度をあげることが第一のように見えたかもしれませんが、ビジネスでは汎化性もとても大切と思います。 実験だけがうまくいき、実ビジネスでは使えないということでは意味がありませんし、精度が高いか低いかはその精度で活かせるかどうかとなります。 データ分析では「どのような特徴が結果に影響しているかを把握する」ということも、とても重要と思います。 結果に何が影響しているかがわかれば、その特徴を管理したり、高めたりすればよくなります。 AutoMLは、結果に対する影響も可視化してくれますので、実ビジネスではむしろこれが何よりも欲しかったという内容なのかもしれません。 PycaretのようなAotoMLの活用によりデータ分析の生産性は大きく変わります。 これは「ややこしそうだな・・・」というだけでスルーしたらダメだと思います。 私はプログラマーでもなく、これまで言語をまともに扱ったこともありませんので、Pythonのスキルはマダマダですが、何がしたいかが明確ならば見よう見まねで何とかできることも多いので、できることから始めて、このAutoMLすばらしさをひとりでも多くの方に味わっていただきたいとことで、この記事を結びたいと思います。   参考サイト 参考書籍
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】importが必要なPythonをAWS Lambda へ移行してみた Lambda編

はじめに 昨日の記事からの続きとなります。 ◆事前準備編◆  1.macOSのデスクトップにtempフォルダを作る  2.tempフォルダにccxtをpip3でインストールする  3.既に作ったpythonプログラムを lambda_function.py へリネームしてtempフォルダに入れる  4.lambda_function.py の先頭に指定プログラムを追記する  5.ターミナルでzipでまとめる ◆Lambda編◆  6.AWSへログインし、5をアップロードする ◆EventBridge編◆  7.EventBridge (CloudWatch Events)に定期実行ルールを登録する 今回は6の説明をします。(次回7を説明します) 手順 6.AWSへログインし、5をアップロードする 6-1.AWSのマネジメントコンソールヘログインします。 https://aws.amazon.com/jp/console/ 6-2.上部の検索バーに Lambda と入力します。Lambda をクリックします。 6-3.右上の関数の作成をクリックします。 (何も関数を作成していない場合は若干画面が異なるかもしれません。) 6-4.[一から作成]が選択されていることを確認します。   [関数名] は任意の名前をつけます。(例:import_ccxt)   [ランタイム] はお使いのPythonのバージョンを指定してください。(例:Python3.9)   下にスクロールし[関数の作成]をクリックします。 6-5.[コードソース]>[アップロード元]を選択し、中から[.zipファイル]を選択します。 6-6.[アップロード]ボタンを押し、手順1−5で作成した.zipファイルを選択し、[開く]をクリックします。   元の画面に戻ったら[保存]をクリックします。 6-7.[テスト]タブをクリックし、名前に任意の名前を記入します。(例:import_ccxt)   [テスト]をクリックします。 6-8.[実行結果:成功] と出ていれば成功です! ハマり注意 ここで [実行結果:失敗] となる場合は特に以下をご確認ください。 ・作成したPythonファイル名が lambda_function.py になっていない ・lambda_function.pyの先頭に以下が入っていない ・zipしたファイルにlambda_function.pyが入っていない lambda_function.py def lambda_handler(event, context): return { } 最後に いかがでしたでしょうか。 無事AWSへPythonを移行できましたでしょうか。 今回はライブラリも同時にuploadしましたので、より実践的かと思います。 次回は定期実行をするためにEventBridgeの使い方を記載しようと思います。 ご参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

最新のツイートをLCDに定期的に表示する人形を作ってみた

Maker Faire Tokyo 2021のブース出展用に『ミニチュアツイート人形』という作品を作成しました。この記事ではこの作品について紹介します。 ミニチュアツイート人形とは ミニチュアツイート人形はRaspberry Pi、グラフィックLCD、Power Automate、kintoneとTwitterを組み合わせた作品です。Twitter上で他人がつぶやかれたツイート情報を定期的にkintoneのアプリに記録させ、ツイートの内容をグラフィックLCDに表示をさせます。作品のガワはブロック玩具で作られています。 作り方 この作品の作り方について案内をします。 1. Raspberry Pi と LCDをつなげる この作品では下記の機器を利用しました: - Raspberry Pi 3 - TG12864B-02WWBV LCD グラフィックディスプレイモジュール 詳細は省きますが、こちらのサイトを参考にしながらRaspberry PiとLCDの配線を行いました。プログラム内ではWiringPiのライブラリを使用しているため、Raspberry PiのピンもWiring Piのピン配置に沿って配線をします。配線はこんな感じです。 TG12864Bは配線も多いので、最初はブレッドボードを間に挟んでプロトタイピングをすると良いです。 ちなみに上の画像、めっちゃ配線間違ってるので真似しないでください。 Raspberry Pi のpythonでLCDに日本語の文字を表示させるにはこちらの記事を参考にさせて頂きました。こちらのコードで指定した文字が表示出来ることを確認した後に、ブレッドボードからユニバーサル基板に乗り換えました。 2. ツイートを溜めるkintoneアプリの作成をする この作品では他人がつぶやかれたツイートの内容をLCDに表示をさせます。Raspberry Pi上でTwitter APIと連携するpythonのコードを書けば、自由度の高いプログラムを作成し、取得したツイートを即時にLCDに表示させることが出来ます。ですが、認証情報を扱う煩わしさや、どうしてもコードが長くなりそうと感じる人もいるでしょう。私みたいに。 その問題を解消するために、ツイートの情報はPower Automate で取得してkintoneに登録することにしました。そして、Raspberry Pi でpythonを使ってシンプルなREST APIのリクエストでkintoneに溜まったツイート情報を取得することにしました。 ツイートの情報を溜めるkintoneアプリは下記のように設計します。『ツイート内容』のフィールド以外は割と適当に設定して大丈夫です。 『ツイート内容』のフィールドコードは『TweetContents』に設定します。この文字列は後ほどpythonのスクリプトから指定する必要があります。 kintoneの環境が無い方は開発者ライセンスの申し込みで無料で取得することが出来ます。アプリの作成方法がわからない場合はこちらのヘルプサイトの記事を参考にしてください。 アプリをkintoneの環境内で公開が出来た後に、次のステップに進みます。 3. Power Automateでツイートをkintoneに登録する 次に、Power Automateの設定をします。 Power Automateには1分に1回フローを実行させることが出来る有能な『Recurrence』という機能があるので、そちらを活用します。環境によっては『繰り返し』という名前になってます。Power Automateの画面から フロー → 新しいフロー → スケジュール済みクラウドフロー と選択すると、この機能を使うことが出来ます。 次のステップに、Twitterの『ツイートの検索』というアクションを設定します。オプションの『検索テキスト』にはTwitterの検索クエリを入れ、『結果の最大件数』には1と入力します。ここの設定はもちろん好きなように変更して良いのですが、今回はこれで進めます。これで1分に1回、検索クエリに該当した最新のツイートを1件取得してくれます。 ちなみにこんなことしなくても、最新のツイート拾ってくれるTwitterのトリガーが存在するから、それを使えば良いんじゃないか?と思われるかもしれません。確かに存在はするのですが、こちらは試してみたところ、実行されるのが1時間に1回でした。ちょっと回数が少ないですね。 続けて、取得したツイートをkintoneに登録する必要があります。ステップを追加し、kintoneを検索し、『アプリにレコードを追加』というアクションを設定します。kintoneのアクションはプレミアムプランですが、Power Appsの開発者向けプランを申し込めば、無料でプレミアムプランのコネクタを活用することが出来ます。 Power Automateの設定画面からkintoneを認証させる必要があります。自分が持ってるkintone環境の設定を変えずに、そのまま認証を進めようとするとほぼ確実にkintoneのエラー画面にが表示されます。 kintoneが外部サービスとの連携を許可しないといけないのですが、こちらのヘルプサイトの記事通りに: 1. kintoneの『cybozu.com共通管理画面』からMicrosoft Power Automate の連携を有効にする 2. 連携利用ユーザの設定で自分のkintoneユーザアカウントにチェックが入るようにする という設定が事前に必要になります。 これさえ設定が出来ていれば、Microsoft Flowの設定画面からkintoneの認証をすることが出来ます。 一度認証が終わり、アクションの設定にアプリIDやTwitterの情報を入れ始めたら、Power AutomateのUIが少し変わって kintoneのアクションが Apply to each という枠に囲まれます。これはTwitterから取得される情報が配列のため、Apply to eachが自動的に設定されるような仕様になってます。まぁ正直あまり気にしなくて良いです。下記のように、『アプリ ID』にはkintoneアプリのID(ブラウザでkintoneアプリを開いた時に、URLに cybozu.com/k/ の後の数字がアプリID)を、『ツイート内容』にTwitterのツイート内容が入るようにし、他は適当に設定してOKです。 Power Automateの設定は保存し、次のステップに進みましょう。 4. LCDにツイート内容を表示する 次にLCDにツイートの内容を表示させます。 ツイートの内容はkintoneに登録されています。Raspberry Pi のpythonからkitnone アプリに対してREST APIを叩き、取得した情報をLCDに流すような感じで進めます。そしてその処理が一定間隔で繰り返すようにします。 こちらの記事で案内された修正されたGistを元に下記のコードの改変を行います。 まず、REST APIを叩くので、必要なモジュールをインポートします。 import requests import json 次に、リクエストに設定するkintone用のパラメータを設定します。 URL = "https://{サブドメイン}.cybozu.com/k/v1/records.json?app={アプリID}&query=order by $id desc limit 1" API_TOKEN = "{APIトークン}" パラメータは自分のkintone環境によって入れる内容がことなります: - {サブドメイン}: 情報を取得するアプリが所属する環境のサブドメイン名を入れてください。ブラウザでkintone環境にログインする時に、URLに表示されているはずです。 - {アプリID}: 情報を取得するアプリのIDを設定します。 - {APIトークン}: 情報を取得するアプリからAPIトークンを生成し、その文字列をここに設定して下さい。APIトークンの生成方法についてはこちらのdeveloper networkの記事を参照してください。 次に、kintone API を叩く関数を作ります。引数として、REST APIのエンドポイントとAPIトークンの値を指定します。 def get_kintone(url, api_token): headers = {"X-Cybozu-API-Token": api_token} resp = requests.get(url, headers=headers) return resp 次に、LCDに表す文字を決定する関数を作ります。複数のレコードを取得するREST APIを叩いているので、レスポンスは配列で返ってきます。一応今回叩くREST APIのクエリでレコードが1件しか返ってこないようにはしていますが、それでもレスポンスは配列で返ってきます。なので、配列の0番目にアクセスし、TweetContentsというプロパティを参照します。こちらのプロパティの文字列はkintoneの『ツイート内容』フィールドのフィールドコードと一致してる必要があります。そのプロパティのvalueを参照すると、ツイート内容の文字列を引き出すことが出来ます。後は参考にしてたQiita記事の方で準備されてた関数を使って、文字列の画像化を行い、LCDにデータを渡します。 def display_tweets(): RESP = get_kintone(URL, API_TOKEN).json() RESP = RESP["records"][0]["TweetContents"]["value"] display_filename = make_textbmp(RESP) lcd_display(display_filename) if __name__ == '__main__' 内でループ処理でkintoneのAPIを叩く関数を呼びます。どれくらいの間隔でREST APIを叩くかは、kintoneのREST APIの制限事項を確認しながら設定しましょう。下記の例では60秒に1回叩いています。 if __name__ == '__main__': args = sys.argv if len(args) != 1: sys.exit while True: display_tweets() time.sleep(60) 5. ガワの作成 最後にガワの作成をしました。これはプログラミングとかは関係なく、ブロックを購入して好きなように組み合わせるだけの作業でした。今回の展示用ブースはvoxel風のテーマで進めていたので、ツイート人形もvoxel風に仕上げました。 kintoneのDevRel関係のキャラとして出雲沙斗美ちゃんというキャラが存在します。いずもさとみ、と読みます。 Maker Faire 用の動画ではゆっくりバージョンの沙斗美ちゃんが作品の紹介をします。 そんな沙斗美ちゃんを今回はvoxel風に仕上げました。 続けて、LCDが固定出来るように、テレビのようにまわりを固めました。 そして最後に、沙斗美ちゃんとLCDのテレビを合体させました。 動作確認 Raspberry Pi のコンソールからpythonのスクリプトを引数無しで実行します。しばらくして、ツイート情報がkintoneアプリから取得され、LCDに表示されます。一分後、新しいツィート情報があれば、LCDの内容が書き換わります。 最後に 今回はツイートを取得する方法はシンプルに設定しています。新しいツイートが無い場合、重複してkintoneにレコードが追加されます。また、1分の間に複数のツイートがあった場合、最新のツイートだけしか拾われません。Power Automate側で工夫を行えば、複数のツイートを一回で取得し、まだ登録してないツイート情報だけkintoneに登録することが出来ます。まだ、python側のスクリプトを工夫すれば最新のkintoneのレコード情報を表示するのではなく、まだLCDに表示していないレコード情報を順番に表示していく、ということも出来ます。 とりあえずこの記事では動くものの紹介をしているので、さらにこだわりを入れたい人は是非挑戦してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCodeでdeclaration(関数などの定義をコードで見る)

udemyで受講しているPythonの講義がPycharmを使用していたが、私が使用しているのは VSCode。いきなり、Go toを開きcapitalizeの定義のコードに飛んだ。 だが、VSCodeでも簡単にできた。選択してFn+F12
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google Cloud Functionsで、GitHubのprivateリポジトリにある自作ライブラリを使う方法

Google Cloud Functions(以後、GCF)でアプリをデプロイするとき、Cloud Build環境でSSHキーにアクセスできません。そのため、GitHubのprivateリポジトリにアクセスできないので、アプリのソースコード上に自作ライブラリをあらかじめ組み込んでおく必要があります(公式リファレンス)。 しかしながら、pip install -tでインストールした方法ではいくつか問題があります。 そこで本記事では、ライブラリをwhlに固めてから組み込む方法を紹介します。 自作ライブラリのソースコードをpip install -tで組み込む問題点 Pipfile, requirements.txtなどで依存ライブラリを一元管理できなくなります。また、自作ライブラリの依存ライブラリがアプリのソースコード下に組み込まれてしまいます。 同じライブラリを二重にインストールすることがあるので、デバッグが困難になり得るほか、アプリの容量が肥大化します。 自作ライブラリをwhlで組み込む whl形式は自作ライブラリの依存ライブラリ一覧も入っているため、前述の問題が解決されます(whlのデータ構造については、付録をご覧ください)。 1. 自作ライブラリからwhlを生成する 自作ライブラリをwhlに変換するには以下のコマンドを使用します。 ※ wheelライブラリが必要なので、必要に応じインストールください。 $ python setup.py bdist_wheel これにより (ライブラリ名)-(バージョン)-py3-none-any.whl などの形式でwhlが生成されます。 2. アプリに自作ライブラリのwhlを依存関係に追加する whlをアプリのディレクトリ内に組み込み、Pipfileやrequirements.txtにwhlを追加します。 pipenv(Pipfile)の場合 以下のコマンドを実行して、Pipfileに追加します。 $ pipenv install ./path/to/(whl名).whl pipenv install後のPipfile ...(前略)... [packages] (パッケージ名) = {path = "./path/to/(whl名).whl"} ...(後略)... requirements.txt 仮想環境ツールを使う、あるいは直接requirements.txtに追記します。 ※ 仮想環境を使っている場合も、デプロイまでにrequirements.txtにwhlを追記しておく必要があります。 ...(前略)... ./path/to/(whl名).whl ...(後略)... 以上により、GCFのデプロイ時に自作ライブラリがインストールされるようになります。pipはwhlのRequires-Distに書いている依存ライブラリを見て依存関係を解決するため、アプリおよび自作ライブラリで依存関係が干渉するリスクが軽減されます。 付録-whlのデータ構造 whlには、ライブラリのソースコードおよびメタデータをバイナリで保持しています。whlはZIPフォーマットなので、ZIPで解凍できます(公式リファレンス)。中を見てみると、 ライブラリ名/: ライブラリのソースコード ライブラリ名-(バージョン).dist-info/: ライブラリのメタデータ が入っています。ライブラリのメタデータの中には最低でも以下のファイルが入っています。 METADATA: ライブラリの概要情報。バージョン、ライブラリ名、作成者情報などが入っている 依存ライブラリの一覧は、METADATAのRequires-Distに記載されている RECORD: whl内ファイルのsha256以上のハッシュ値があり、whlの整合性が問題ないかの確認に使う WHEEL: whl形式のバージョンや生成方法など METADATAの例は以下となります。 Metadata-Version: 2.1 Name: (ライブラリ名) Version: (ライブラリのバージョン) Summary: (ライブラリの説明文) Home-page: (ライブラリ制作元の管理ページ) Author: (ライブラリ製作者名) License: (ライブラリのライセンス) Platform: ([ライブラリが対応するCPUアーキテクチャ。ソースコードおよび依存関係でC言語拡張を含む場合、明示的に指定が必要になる](https://www.python.org/dev/peps/pep-0425/)) Requires-Dist: google-cloud-tasks (==1.5.0) 以上、ご覧いただきありがとうございました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonistaのためのC++記法入門: それC++ではどう書くの6 ~関数定義1~

PythonistaのためのC++記法入門: それC++ではどう書くの5の続き。今回からは関数について。 関数関連 関数の基本 例として 1. 整数型の2値を受け取ってその和(整数型)を返す関数 2. 整数型の2値を受け取ってその平均(整数型とは限らない)を返す関数 を考える。 Pythonではこう書いた def sum(x, y): ans = x + y return ans def average(x, y): ans = sum(x, y) / 2. return ans def main(): a = 10 b = 20 print(sum(a, b)) # 30 が出力される print(average(a, b)) # 15.0 が出力される if __name__ == '__main__': main() Pythonでは引数の型も、返り値の型も何も気にしなくてよい。その代わり、この場合だと引数や返り値の型が狙い通りになるか確証はない。 C++ではこう書く #include <bits/stdc++.h> using namespace std; int sum(int a, int b) { int ans; ans = a + b; return ans; } float average(int a, int b) { float ans; ans = sum(a, b) / 2.; return ans; } int main() { int a = 10; int b = 20; cout << sum(a, b) << endl; // 30 が出力される cout << average(a, b) << endl; // 15 が出力される(表記上int型に見えるがtypeidはf,つまりfloat型) } 一方C++では引数や返り値の型を考慮した上で関数を定義する必要がある。 整数型の2値の平均は常に整数型になるとは限らないため、floatを返り値に取るように関数を定義しないと所望の答えが得られない。 (一応)Pythonでもこう書ける Python3.5以降で導入された型ヒントを用いることで型を考慮した関数定義ができるようになった。スクリプトが複雑化し、どの関数がどういった意味を持ち、何を引数に取って何を返すべきかがわかりにくくなってきたときに地味に便利である。 def sum(x: int, y: int) -> int: ans = x + y return ans def average(x: int, y: int) -> float: ans = sum(x, y) / 2. return ans 返り値がない関数 PythonでもC++でもreturn文を省略できるのは同じ。 Pythonではこう書いた def display_string(x): print(x) def main(): display_string("This will be displayed.") if __name__ == '__main__': main() 実質的にNoneを返している。型ヒントを使う場合はdef func(x) -> None:と書く。 C++ではこう書く #include <bits/stdc++.h> using namespace std; void display_string(string x) { cout << x << endl; } int main() { display_string("This will be displayed."); // This will be displayed. と出力される } C++では関数定義時に返り値の情報が必要なので、このような場合はvoidを充てる(個人的にこのワードチョイスは絶妙だと思う)。 デフォルト値を指定する Pythonではこう書いた def greet(message, name="John", goal=1): print(message, end=" ") print(f"My name is {name}. I want to be No.{goal}.") def main(): greet("Yeah!!!") greet("Hi.", "Mike") greet("Well...", "Bob", 2) if __name__ == '__main__': main() #include <bits/stdc++.h> using namespace std; void greet(string message, string name="John", int goal=1) { cout << message; cout << " My name is " << name << ". I want to be No." << goal << "." << endl; } int main() { greet("Yeah!!!"); greet("Hi.", "Mike"); greet("Well...", "Bob", 2); } ほとんど書き方や使い方は同じ。出力はいずれも以下のようになる。 Yeah!!! My name is John. I want to be No.1. Hi. My name is Mike. I want to be No.1. Well... My name is Bob. I want to be No.2. オーバーロード(overload) C++ではこう書く C++では同名の関数を定義できる。これはPythonでは(異なる名前空間を用いない限りは)できない。 オーバーロードによる多重定義ができる関数は、「引数の型」か「引数の数」が異なる場合である。 よって、以下のように二つのsum関数を用意することができる(意義は薄い)。 int sum(int a, int b){ int ans; ans = a + b; return ans; } int sum(int a, int b, int c){ int ans; ans = a + b + c; return ans; } ラムダ式 Pythonではこう書いた my_min = lambda x, y: x if x < y else y すなわち 関数名 = lambda 引数1, 引数2, ...: 処理 C++ではこう書く auto my_min = [](int a, int b) { int answer= a < b ? a : b; return answer; }; すなわち auto 関数名 = [](引数1の型 引数1, 引数2の型 引数2, ...) { 処理; return 返り値; };
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonistaのためのC++記法入門: それC++ではどう書くの5 ~条件文~

PythonistaのためのC++記法入門: それC++ではどう書くの4の続き。今回は短め。 if文 Pythonではこう書いた if a == 10: # 処理A elif a == 100: # 処理B else: # 処理C bool値に関しては以下のように書ける Pythonでは0や空文字列""、空のlistなどもFalseとして扱われる。 flag = True if flag: # 処理 flag = False if not flag: # 処理 C++ではこう書く int a; if (a == 10) { // 処理A } else if (a == 100) { // 処理B } else { // 処理C } bool値に関しては以下のように書ける。 bool flag = true; if (flag) { # 処理 } flag = false; if (!flag) { # 処理 } if文の書き換え Pythonではこう書いた a, b = 10, 20 if a < b: answer = a else: answer = b # 以下のように1行で書ける answer = a if a < b else b C++ではこう書く int a(10), b(20); int answer; if (a < b) { answer = a; } else { answer = b; } // 以下のように1行で書ける int answer = a < b ? a : b; まとめ C++ Python if文 if (条件1) {  処理1;}else if (条件2) {  処理2;}else {  処理3;} if 条件1:    処理1elif 条件2:    処理2else:    処理3 boolean判定(1) if (boolean){  処理} if boolean:    処理 boolean判定(2) if (!boolean){  処理} if not boolean:    処理 三項演算子 型 変数 = 条件文? true時の値 : false時の値 変数 = True時の値 if 条件文 else False時の値 and / or Pythonではこう書いた if a > 0 and b > 100: # 処理 if a > 0 or b > 100: # 処理 C++ではこう書く if (a > 0 && b > 100) { # 処理 } if (a > 0 || b > 100) { # 処理 } for文 Pythonではこう書いた for a in range(0, N, 1): # 処理 # 一般的にはこう書くことのほうが多い for a in range(N): # 処理 C++ではこう書く for (int i = 0; i < N; i++) { // 処理 } コンテナ型に関しては以下のようにも書ける。 条件文の配列の要素の型 変数名 : 配列変数に注意。 配列の要素の型を常に考えないといけない。 vector<int> a = {1, 3, 2, 5}; for (int x : a) { // 処理 } autoを使うと簡単に書ける。 vector<int> a = {1, 3, 2, 5}; for (auto x : a) { // 処理 } イテレータを用いた書き方は別記事にて説明する。 while文 Pythonではこう書いた while i < 10: # 処理 i += 1 C++ではこう書く int i = 0; int N = 10; while (i < N) { // 処理 i++; }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fundamental Statistics for Data Science (Part 02)

continuation from Fundamental Statistics for Data Science (Part 01). 5) Mean Mean is also famous as average in mathematics, which can be obtained by summing all the observations and divided by the number of data points. Let us think that a random variable has the following data. N is the number of data points. X = { x1, x2, x3, . . . , xN } We can calculate the mean using the following equation. mean.py import numpy as np x = np.array([1,2,3,4,5,6]) x_mean = np.mean(x) print(x_mean) 6) Variance Variance refers to a spread between numbers in a data set. More pointedly, variance measures how far each number in the set is from the mean value in the dataset. When we calculated the sample variance, we can use it to approximate the population variance. var.py import numpy as np x = np.array([1,4,3,6]) x_variance = np.var(x) print(x_variance) 7) Standard Deviation Standard deviation measures the distribution of a dataset relative to its mean. We can calculate it by the square root of the variance. Standard deviation is usually favored over the variance since it has the same unit as the data points, implying we can interpret it more easily. std.py import numpy as np x = np.array([1,4,3,6]) x_std = np.std(x) print(x_std) Let's continue from part 03 *本記事は @qualitia_cdevの中の一人、@nuwanさんが書いてくれました。 *This article is written by @nuwan a member of @qualitia_cdev.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonでブロックで実行時間をログに残る

背景 pythonでDB query実行時間などを残りたい いつでもend - startで時間を計算することが面倒 普通のやり方 time_sta = time.perf_counter() time.sleep(2) time_end = time.perf_counter() print(time_end - time_sta) 対策 rubyのブロックように実装して便利になると思う pythonではrubyのblockのような予約語がない 実装してみる from contextlib import contextmanager import time import sys @contextmanager def time_count(function_name: str): time_sta = time.perf_counter() yield time_end = time.perf_counter() print(f"{function_name}の実行時間: {time_end - time_sta}") def get_tenant_info(): with time_count2(sys._getframe().f_code.co_name): time.sleep(2) get_tenant_info() # -> get_tenant_infoの実行時間: 2.0020375240128487 メリット ブロックで実行時間をログに残って、実装方法を固定できる ソードコードの量が少なくなって、バグもない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

犬と猫の分類をPytorchでファインチューニングをしてやってみた

はじめに コチラの書籍でPytorchの勉強をしているのですが、実際に使わないと理解できないと思ったので、Kaggleの犬猫コンペをPytorchを使ってやってみた記録です。 モデルにはefficientnetB7を使ってファインチューニングを行いました。 環境 Google colabを使います。 Pytorch: 1.9.0+cu102 python: 3.7.11 実装の流れ 1.データの用意 2.データの前処理 3.データセットの作成 4.データローダーの作成 5.モデルの構築 6.損失関数、最適化アルゴリズムの定義 7.学習・検証 8.テストデータで推論 となります。1つずつ紹介します。 1. データの用意 今回の犬猫コンペの画像データは容量が重いのでGoogleDriveにアップロードするのもそこからデータ読み込ませるのも時間がかかります。 ですのでcolabのcontent直下に直接Kaggle APIを利用してデータをダウンロードします。 from google.colab import files uploaded = files.upload() for fn in uploaded.keys(): print('User uploaded file "{name}" with length {length} bytes'.format( name=fn, length=len(uploaded[fn]))) # Then move kaggle.json into the folder where the API expects to find it. !mkdir -p ~/.kaggle/ && mv kaggle.json ~/.kaggle/ && chmod 600 ~/.kaggle/kaggle.json 上記コードを実行すると jsonファイルをアップロードするように言われるのでkaggle.jsonファイルをアップロードします。 Saving kaggle.json to kaggle.json User uploaded file "kaggle.json" with length 63 bytes こんな表示がされればokです。 !kaggle competitions download -c dogs-vs-cats-redux-kernels-edition コンペのDataのページに表示されているコードをそのまま実行すればデータをダウンロードしてくれます。 ただこのままだとzipファイルのままなのでzipファイルを解凍する必要があります。 import zipfile with zipfile.ZipFile('/content/train.zip') as existing_zip: existing_zip.extractall('/content/train_img/') with zipfile.ZipFile('/content/test.zip') as existing_zip: existing_zip.extractall('/content/test_img/') zipfileモジュールを利用して、train_imgとtest_imgという名前で解凍してます。 データセットの用意の前に必要なものをimportしておきます import numpy as np import random import glob from PIL import Image import torch import torch.nn as nn import torch.optim as optim import torch.utils.data as data import torchvision from tqdm import tqdm from torchvision import models, transforms from sklearn.model_selection import train_test_split また再現性を担保するため乱数シードも設定します。 torch.manual_seed(46) np.random.seed(46) random.seed(46) 櫻坂46が好きなので、乱数シードはいつも46です。 2. データの前処理 前処理を行うImageTransformクラスを定義します。 class ImageTransform(): def __init__(self, resize, mean, std): self.data_transform = { 'train': transforms.Compose([ transforms.Resize((resize, resize)), transforms.RandomHorizontalFlip(p=0.5), transforms.RandomCrop(size =(150, 150), padding=18), transforms.ToTensor(), transforms.Normalize(mean,std) ]), 'val': transforms.Compose([ transforms.Resize((resize,resize)), transforms.ToTensor(), transforms.transforms.Normalize(mean, std) ]), 'test' : transforms.Compose([ transforms.Resize((resize,resize)), transforms.ToTensor(), transforms.transforms.Normalize(mean, std) ]) } def __call__(self, img, phase): return self.data_transform[phase](img) trainデータ、valデータ、testデータそれぞれに行う前処理の内容をComposeを使ってまとめ、辞書化しております。 trainデータに関しては150×150にリサイズ、ランダム水平反転、ランダムクロップ、テンソル化、正規化を行なっています。 valデータとtestデータに関しては、水増しは行わず、リサイズとテンソル化、正規化だけ行います。 3.データセットの用意 次にDatasetを作成するDogCatDatasetクラスを定義します。 class DogCatDataset(data.Dataset): def __init__(self, file_list, phase, transform = None): self.file_list = file_list self.transform = transform self.phase = phase def __len__(self): return len(self.file_list) def __getitem__(self, index): img_path = self.file_list[index] img = Image.open(img_path) img_trans = self.transform( img, self.phase) label = img_path.split('/')[-1].split('.')[0] if label == 'cat': label = 0 elif label == 'dog': label = 1 return img_trans, label 最終的なスコア提出の際に、犬である確率を提出する必要があるので、ラベルはcatを0,dogを1としています。 普段、画像の読み取りには、cv2を使ってたのでPILのImageに慣れるのに苦労しました。 実際に定義したクラスを使ってデータセットを作成していきます。 train_list = glob.glob('/content/train_img/train/*') test_path_list = glob.glob('/content/test_img/test/*') trainデータとtestデータの画像パスを全部取得してリスト化します。 trainデータのうち25%を検証データとするため、画像パスをtrain_test_splitを使って分割します。 分けられたインデックスを使って、trainとvalの画像パスをリスト化します。 train_idx, valid_idx = train_test_split(range(len(train_list)), test_size = 0.25, random_state = 46) train_path_list = [] val_path_list = [] for index in train_idx: i = train_list[index] train_path_list.append(i) for index in valid_idx: i = train_list[index] val_path_list.append(i) 画像のインデックス番号を分ける使い方は初めてだったのでちょっと感動しました。 len(train_path_list), len(val_path_list), len(test_path_list) # -> (18750, 6250, 12500) データの枚数をチェックしておきましょう。 さて画像パスをリスト化できたので、このリストを元にdatasetを作ります。 size = 150 mean = (0.5,0.5,0.5) std = (0.5,0.5,0.5) train_dataset = DogCatDataset( file_list = train_path_list, transform = ImageTransform(size, mean, std), phase = 'train') val_dataset = DogCatDataset( file_list = val_path_list, transform = ImageTransform(size, mean, std), phase = 'val') test_dataset = DogCatDataset( file_list = test_path_list, transform = ImageTransform(size, mean, std), phase = 'test') 今回全画像データの平均や標準偏差を計算するのがめんどくさかったので良くみられる、全て0.5で処理しています。 4. データローダーの作成 datasetが作成できれば、ここは簡単です。 batch_size = 64 train_dataloader = data.DataLoader( train_dataset, batch_size=batch_size, shuffle = True) val_dataloader = data.DataLoader( val_dataset, batch_size=batch_size, shuffle = False) test_dataloader = data.DataLoader( test_dataset, batch_size=batch_size, shuffle = False) dataloaders_dict = {'train': train_dataloader, 'val': val_dataloader, 'test': test_dataloader} 後でdataloaderを簡単に取り出しやすいようにまとめて辞書化しておきます。 5. モデル構築 まずPytorchでeffiientnetを使うために学習済みモデルをインストールします !pip install efficientnet_pytorch Efficientnet-b7をインスタンス化します。 from efficientnet_pytorch import EfficientNet model = EfficientNet.from_pretrained('efficientnet-b7') 最後の全結合層を今回は2値分類なので出力ユニットを変更します。 num_ftrs = model._fc.in_features model._fc = nn.Linear(num_ftrs, 2) 6. 損失関数、最適化アルゴリズムの定義 loss_fn = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr = 0.1, momentum=0.9) scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=EPOCHS) 損失関数はCrossEntropyLoss 最適化アルゴリズムはファインチューニングだとモーメンタム付きSGDが良さげとのことなので使用。 また学習率スケジューラーも使用します。 7. 学習・検証 学習を行う関数を定義します。 def train_model(model, dataloaders_dict, loss_fn, optimizer, epochs, scheduler): history = {'loss':[], 'acc':[], 'val_loss':[], 'val_acc':[]} device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") model = model.to(device) for epoch in range(epochs): print(f'{epoch + 1} start') print('----------') for phase in ['train', 'val']: if phase == 'train': model.train() else: model.eval() epoch_loss = 0.0 epoch_corrects = 0.0 if (epoch == 0) and (phase == 'train'): continue for inputs, labels in tqdm(dataloaders_dict[phase]): inputs = inputs.to(device) labels = labels.to(device) optimizer.zero_grad() with torch.set_grad_enabled(phase == 'train'): outputs = model(inputs) loss = loss_fn(outputs, labels) pred_value, pred_label = torch.max(outputs, 1) if phase == 'train': loss.backward() optimizer.step() epoch_loss += loss.item() * inputs.size(0) epoch_corrects += torch.sum(pred_label == labels.data) if phase == 'train': scheduler.step() epoch_loss /= len(dataloaders_dict[phase].dataset)*1.0 epoch_acc = epoch_corrects.double() / len(dataloaders_dict[phase].dataset)*1.0 print(f'{phase} Loss: {epoch_loss :.4f} Acc : {epoch_acc :.4f}') if phase == 'train': history['loss'].append(epoch_loss) history['acc'].append(epoch_acc) else: history['val_loss'].append(epoch_loss) history['val_acc'].append(epoch_acc) Pytorchだと学習時に必要な情報を自分でカスタマイズできるので、好きですね。 じゃ学習を実行します。 EPOCHS = 100 train_model(model, dataloaders_dict, loss_fn, optimizer, epochs=EPOCHS, scheduler) 100エポック終わらせるのに7時間くらいかかりました。 8. テストデータで推論 labels = [] ids = [] cat_preds = [] dog_preds = [] with torch.no_grad(): for image_path in tqdm(test_path_list): img = Image.open(image_path) test_transformer = ImageTransform(size, mean, std) img = test_transformer(img, phase = 'test') img = img.unsqueeze(0) img = img.to(device) model.eval() output = model(img) pred = nn.functional.softmax(output, dim = 1)[:].tolist() cat_preds.append(pred[0][0]) dog_preds.append(pred[0][1]) ids.append(int(image_path.split('/')[-1].split('.')[0])) このうちのdog_predsリストに犬である確率が格納されているのでこれを提出します。 import pandas as pd submit = pd.DataFrame({'id': ids, 'label': dog_preds}) submit.sort_values(by='id', inplace=True) submit.reset_index(drop=True, inplace=True) submit.to_csv('/content/drive/MyDrive/dog vs cat/dogs-vs-cats-redux-kernels-edition/submission8.csv', index=False) スコア⇩ まとめ 普段Tensorflowを使ってますが、Pytorchだとコードは長くなる分、丁寧に記述していくので、理解が深まるなと思った。他の深層学習(GAN、物体検出など)もPytorchを使って実装していきたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gremlin-server コンテナを立ち上げる② ー neo4j embeded

はじめに gremlin-server コンテナを立ち上げる の続編です。 neo4j のファイル群を使って、gremlin サーバ上のグラフデータを永続化する方法について整理します。 TL; DR 勘違いしていた点 BOLT 接続は、リモートNeo4jサーバ へプロキシすると思っていたが、そうではない Cypher を gremlin で使えるようにするらしい (試していないです・・) 設定ファイルの変更点 gremlin-server.yml をベースに、gremlin-server-neo4j.yml を作成 もちろん、サンプルの gremlin-server-neo4j.yaml も参考 docker-compose.override.yml の entrypoint の設定ファイルパスを変更 Python 経由で使っても、問題なく動作する コード一式(設定ファイル変更前)は、こちら 環境 対象 バージョン OS macOS Big Sur (11.5.2) docker Docker version 20.10.8, build 3967b7d docker-compose docker-compose version 1.29.2, build 5becea4c python Python 3.8.10 やったこと 設定ファイルを変更し、docker-compose.override.yml で設定ファイルへのパスを変更しました。 設定ファイルの変更 gremlin-server.yml との差分 ~/p/gremlin ❯❯❯ diff conf/gremlin/server/gremlin-server{,-neo4j}.yml ✘ 1 17a18,27 > # Note that TinkerPop does not include Neo4j dependencies in its > # distributions. This file cannot be used as a configuration file to > # Gremlin Server unless Neo4j dependencies are installed on the > # Gremlin Server path with: > # > # bin/gremlin-server.sh -i org.apache.tinkerpop neo4j-gremlin x.y.z > # > # Note that unless under a commercial agreement with Neo4j, Inc., > # Neo4j is licensed AGPL. > 23c33 < graph: conf/tinkergraph-empty.properties} --- > graph: conf/neo4j.properties} 27c37 < org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: {}, --- > org.apache.tinkerpop.gremlin.neo4j.jsr223.Neo4jGremlinPlugin: {}, ~/p/gremlin ❯❯❯ .properties ファイルの指定を変更し、Neo4jGremlinPlugin のところを変えました。(前半の差分は、コメントなので略) docker-compose.override.yml の変更 以下に、サービス gremlin-server の部分だけを抜粋します。 gremlin-server: build: context: ./ dockerfile: docker/gremlin/server/Dockerfile image: $project.gremlin-server container_name: $project.gremlin-server ports: - "8182:8182" volumes: - ./data/:/data/ - ./conf/gremlin/server/gremlin-server.yml:/opt/gremlin-server/conf/gremlin-server.yml - ./conf/gremlin/server/gremlin-server-neo4j.yml:/opt/gremlin-server/conf/gremlin-server-neo4j.yml - ./conf/gremlin/server/neo4j.properties:/opt/gremlin-server/conf/neo4j.properties entrypoint: > /bin/bash bin/gremlin-server.sh conf/gremlin-server-neo4j.yml # /bin/bash bin/gremlin-server.sh conf/gremlin-server.yml 永続化確認 github リポジトリ をクローンし、上記の設定変更が終わっているとします。 サービス群の起動 make サービス群の起動確認 起動できていることを確認します。以下のように gremlin.gremlin-server が起動していればOKです。 ※ gremlin.app は、python の VSCode用開発環境用のコンテナ(Ubuntu 20.04)です ~/p/gremlin ❯❯❯ make ps docker-compose ps Name Command State Ports ---------------------------------------------------------------------------------------------------------- gremlin.app /bin/bash Up gremlin.gremlin-server /bin/bash bin/gremlin-serv ... Up 0.0.0.0:8182->8182/tcp,:::8182->8182/tcp ~/p/gremlin ❯❯❯ データがないことを確認 ~/p/gremlin ❯❯❯ make gremlin-console < src/gremlin/ref.gremlin (省略) gremlin> g.V().values('name') gremlin> g.V().count() ==>0 gremlin> :exit ~/p/gremlin ❯❯❯ データ投入 ~/p/gremlin ❯❯❯ make gremlin-console < src/gremlin/test.gremlin (省略) gremlin> :remote connect tinkerpop.server conf/remote.yml ==>Configured gremlin-server/172.24.0.2:8182 gremlin> :remote console ==>All scripts will now be sent to Gremlin Server - [gremlin-server/172.24.0.2:8182] - type ':remote console' to return to local mode gremlin> :remote connect tinkerpop.server conf/remote.yml ==>Configured gremlin-server/172.24.0.2:8182 gremlin> :remote console ==>All scripts will now be sent to Gremlin Server - [gremlin-server/172.24.0.2:8182] - type ':remote console' to return to local mode gremlin> g.addV('person').property('name','hello') ==>v[38] gremlin> g.addV('person').property('name','world') ==>v[39] gremlin> g.V().values('name') ==>hello ==>world gremlin> g.V().count() ==>2 gremlin> :exit ~/p/gremlin ❯❯❯ 2 ノードが追加されていることがわかります。 再起動(停止&起動) 永続化されていることを確認するため、一度 再起動(停止&起動)をしておきます。 ~/p/gremlin ❯❯❯ make reup docker-compose down Stopping gremlin.gremlin-server ... done Stopping gremlin.app ... done Removing gremlin.gremlin-server ... done Removing gremlin.app ... done Removing network gremlin_default docker-compose up -d app gremlin-server Creating network "gremlin_default" with the default driver Creating gremlin.app ... done Creating gremlin.gremlin-server ... done ~/p/gremlin ❯❯❯ 再起動後の確認(永続化確認) ~/p/gremlin ❯❯❯ make gremlin-console < src/gremlin/ref.gremlin (省略) gremlin> :remote connect tinkerpop.server conf/remote.yml ==>Configured gremlin-server/172.25.0.3:8182 gremlin> :remote console ==>All scripts will now be sent to Gremlin Server - [gremlin-server/172.25.0.3:8182] - type ':remote console' to return to local mode gremlin> g.V().values('name') ==>hello ==>world gremlin> g.V().count() ==>2 gremlin> :exit ~/p/gremlin ❯❯❯ gremlin-server の 再起動(起動&停止)後でも、再起動前のデータが入っていることが確認できました。 つまり、永続化できていることを意味します。 python で動作確認 めでたく永続化できたので、Python から使っても、特段異常が起きないことを確認します。 まず、import と Gremlin 操作を 隠蔽するクラス(GremlinProvider)を作っておきます。 from gremlin_python.process.anonymous_traversal import traversal from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection class GremlinProvider(object): def __init__(self): self.g = traversal().withRemote( DriverRemoteConnection("ws://gremlin-server:8182/gremlin", "g") ) def clear_nodes(self): self.g.V().drop().iterate() return self def add_person(self, name: str): node = self.g.addV("person").property("name", name).next() return node def add_age(self, name: str, age: int): assert name and 0 <= age <= 150 node = self.g.V().has("person", "name", name).property("age", age).next() return node def add_job(self, name: str, job: str): assert name and job node = self.g.V().has("person", "name", name).property("job", job).next() return node def get_name(self, v): name = self.g.V(v).values("name").toList()[0] return name def get_age(self, v): age = self.g.V(v).values("age").toList()[0] return age def add_knows(self, v1, v2, weight=0.75): edge = self.g.V(v1).addE("knows").to(v2).property("weight", weight).iterate() return edge def find_person_node(self, name: str): node = self.g.V().has("person", "name", name).next() return node def find_person_whom_knows(self, center: str): whom_knows = self.g.V().has("person", "name", center).out("knows").toList()[0] return whom_knows 次が、実験コードです。 if __name__ == "__main__": grp = GremlinProvider() grp.clear_nodes() # marko を登録 v1 = grp.add_person("marko") assert grp.get_name(v1) == "marko" print("v1:", grp.get_name(v1)) # v1: marko # stephen を登録 v2 = grp.add_person("stephen") assert grp.get_name(v2) == "stephen" print("v2:", grp.get_name(v2)) # v2: stephen # 年齢を追加・更新 grp.add_age("marko", 35) # insert grp.add_age("marko", 31) # update grp.add_age("stephen", 32) # 職業を追加・更新 grp.add_job("marko", "SWE") grp.add_job("stephen", "SWE") # リレーション(knows) を追加 e1 = grp.add_knows(v1, v2, 0.1) print(e1) # [['V', v[34]], ['addE', 'knows'], ['to', v[35]], ['property', 'weight', 0.1], ['none']] # marko を検索 marko = grp.find_person_node("marko") print("marko:", grp.get_name(marko)) # marko: marko # marko が知っている人を検索 v = grp.find_person_whom_knows("marko") print("marko knows:", grp.get_name(v)) print("marko knows2:", grp.g.V(v1).outE().inV().values("name").toList()[0]) # marko knows: stephen # marko knows2: stephen # ノードオブジェクトから年齢を取得 print("marko.age:", grp.get_age(v1)) assert grp.get_age(v1) == 31 print("stephen.age:", grp.get_age(v2)) assert grp.get_age(v2) == 32 # marko.age: 31 # stephen.age: 32 # 職業が ”SWE" である人物の名前を取得(リスト) print("SWE:", grp.g.V().has("person", "job", "SWE").values("name").toList()) # SWE: ['marko', 'stephen'] 上記の2ブロックのコードを、1つのファイル(src/trial2.py)にして実行します。 (make bash を実行後、python -m src.trial2 を実行) ~/p/gremlin ❯❯❯ make bash docker-compose up -d app gremlin-server gremlin.app is up-to-date gremlin.gremlin-server is up-to-date docker-compose exec app bash dsuser@7d907178380d:~/workspace$ python -m src.trial2 v1: marko v2: stephen [['V', v[36]], ['addE', 'knows'], ['to', v[37]], ['property', 'weight', 0.1], ['none']] marko: marko marko knows: stephen marko knows2: stephen marko.age: 31 stephen.age: 32 SWE: ['marko', 'stephen'] dsuser@7d907178380d:~/workspace$ 以上により、ざっくりと、以下のことが確認できたことになります。 node オブジェクトの生成 与えられた node オブジェクトの name プロパティを取得 与えられた 2つの node オブジェクトから edge オブジェクトの生成 name から node オブジェクトを検索(取得) 与えられた node オブジェクトから、edge を辿って、エッジの先の node オブジェクトを取得 既に登録した node に対して、プロパティを追加・更新(age, job) ノードオブジェクトから、各種プロパティ(age, job)を取得 特定のプロパティ値を持つ node の name リストを取得 まとめ 永続化のために、Neo4j ファイルを使うようにするには、設定ファイルを少し変更するだけで対応できる BOLT 接続は、リモートの Neo4j に接続するためのものではない(らしい/未確認)点に注意 ある程度、Graph で操作したいことを、Python から実行できることを確認( See src/trial2.py ) node, property へのアクセス、追加、更新は概ね問題ない 参考文献 Gremlinと戯れる gremlinpythonの使い方覚書 Recipes - tinkerpop.apache.org
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSでAIサービスを使ってみる〜第10回lex編その2〜

前回ファイルの実行と今回 前回のlex_create_bot.pyファイルを実行していませんでした。 今回を前回ファイルを実行しbotを作成。botと文字で会話するプログラムを解説し、botを動かして行きます。 前回ファイル lex_create_bot.py import boto3 import time iam = boto3.client('iam') iam.create_service_linked_role(AWSServiceName='lex.amazonaws.com') lex = boto3.client('lex-models', 'us-east-1') #フレーバーのスロットタイプの作成 flavor_slot_type = lex.put_slot_type( name='FlavorSlotType', enumerationValues=[ {'value': 'vanilla'}, {'value': 'chocolate', 'synonyms': ['choc']}, {'value': 'strawberry', 'synonyms': ['berry']} ], valueSelectionStrategy='TOP_RESOLUTION', createVersion=True) print('slot type:', flavor_slot_type['name']) #容器のスロットタイプを作成 container_slot_type = lex.put_slot_type( name='ContainerSlotType', enumerationValues=[ {'value': 'corn'}, {'value': 'cup'} ], valueSelectionStrategy='TOP_RESOLUTION', createVersion=True) print('slot type:', container_slot_type['name']) #インテントの作成 intent = lex.put_intent( name='OrderIntent', #インテント内のスロット slots=[ { 'name': 'Flavor', 'slotConstraint':'Required', 'slotType':'FlavorSlotType', 'slotTypeVersion': '1', 'valueElicitationPrompt': { 'messages': [{ 'contentType': 'PlainText', 'content': 'Vanilla, chocolate or strawberry?' }], 'maxAttempts': 3 } }, { 'name': 'Container', 'slotConstraint': 'Required', 'slotType': 'ContainerSlotType', 'slotTypeVersion': '1', 'valueElicitationPrompt': { 'messages': [{ 'contentType': 'PlainText', 'content': 'Corn or cup?' }], 'maxAttempts': 3 } } ], #発話例 sampleUtterances=[ 'I want {Flavor} ice cream in {Container}', '{Flavor} ice cream {Container}', 'ice create' ], #完了時のセリフ conclusionStatement={ 'messages': [{ 'contentType': 'PlainText', 'content': 'OK, {Flavor} ice cream in {Container}' }], }, #完了の動作 fulfillmentActivity={'type': 'ReturnIntent'}, createVersion=True) print('intent:',intent['name']) #ボットの作成 bot = lex.put_bot( name ='MyBot', locale='en-US', childDirected=False, #インテント intents=[ { 'intentName': 'OrderIntent', 'intentVersion': '1' } ], #中止時のセリフ abortStatement={ 'messages':[ { 'contentType': 'PlainText', 'content': 'Please try again.' } ] }, voiceId='Joanna', createVersion=True) print('bot:', bot['name']) #ボット作成の進捗表示 start = time.time() status = '' while status not in ['READY', 'FAILED']: #ボットを取得 status = lex.get_bot(name='MyBot', versionOrAlias='1')['status'] time.sleep(10) print('{:7.2f} {}'.format(time.time()-start, status)) #作成に失敗した場合は理由を表示 if status == 'FAILED': print(lex.get_bot( name='Mybot', versionOrAlias='1')['failureReason']) #ボットエイリアスを作成 bot_alias = lex.put_bot_alias( name='MyBotAlias', botName='MyBot', botVersion='1') print('bot alias:', bot_alias['name']) 実行しbotを作成します python lex_create_bot.py 実行結果 botが作成されました。筆者は先にロールやスロットを作成し、同じロールやスロットを何度も作成し怒られて時間がかかりました。(ちなみに先にロールやスロットを作してしまった際には作成した部分をコメントアウトしてスキップして下さい) botが作成されたのでbotと文字で会話するプログラムを作成します。 文字でbotと会話する 以下コードになります。 lex_chat_text.py import boto3 import uuid #①lex-runtimeサービスクライアント作成 lex_runtime = boto3.client('lex-runtime', 'us-east-1') #②ユーザーIDの作成 user = str(uuid.uuid1()) #③インテントが完了するまで続ける state = '' while state != 'Fulfilled': result = lex_runtime.post_text( botName='MyBot', botAlias='MyBotAlias', userId=user, inputText=input('You: ')) #ボットの応答の表示 print('Bot:', result['message']) #会話の状態を取得 state = result['dialogState'] #④取得したスロットの値の表示 print() print('Flavor :', result['slots']['Flavor']) print('Container :', result['slots']['Container']) ①lex_runtimeのサービスクライアントを作成します。bot作成ではlexのサービスクライアントを作成しますが、botの会話にはlexRuntimeサービスクライアントを使います。 ②ユーザーIDの作成 uuidを用いてランダムなユーザーIDを作成する。 ③会話の状態がFulfilledになるまで会話を続ける。 post_textメソッドを用いてbotに文字列を送信する。post_textメソッドの戻り値はresultに代入されます。result['message']でbotの応答を指定したりresult['dialogState']で会話の状態を取得します。 ④スロットの値を表示 post_textメソッドの戻り値であるresult内の['Flavor']や['Container']を取得して画面に表示します。 botとの文字での会話の実行 会話ファイルを実行しチョコレートアイスを注文、するとbotがcornかcupか聞いてcornと答えるとbotから最終の注文を答えてくれました。 補足ですがFlavorのみ答えても無視されます(笑) iceというワードが入ると認識してくれましたね、どうやらちゃんとワードを与えないとシカトされる可能性もあるのでお忘れなく。 まとめ 今回はlexを用いて簡単なbot作成とbotとの会話をご紹介しました。 参考文献 この記事は以下の情報を参考にして執筆しました AWSでつくるAIプログラミング入門
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSでAIサービスを使ってみる〜⑩lex編その2〜

前回ファイルの実行と今回 前回のlex_create_bot.pyファイルを実行していませんでした。 今回を前回ファイルを実行しbotを作成。botと文字で会話するプログラムを解説し、botを動かして行きます。 前回ファイル lex_create_bot.py import boto3 import time iam = boto3.client('iam') iam.create_service_linked_role(AWSServiceName='lex.amazonaws.com') lex = boto3.client('lex-models', 'us-east-1') #フレーバーのスロットタイプの作成 flavor_slot_type = lex.put_slot_type( name='FlavorSlotType', enumerationValues=[ {'value': 'vanilla'}, {'value': 'chocolate', 'synonyms': ['choc']}, {'value': 'strawberry', 'synonyms': ['berry']} ], valueSelectionStrategy='TOP_RESOLUTION', createVersion=True) print('slot type:', flavor_slot_type['name']) #容器のスロットタイプを作成 container_slot_type = lex.put_slot_type( name='ContainerSlotType', enumerationValues=[ {'value': 'corn'}, {'value': 'cup'} ], valueSelectionStrategy='TOP_RESOLUTION', createVersion=True) print('slot type:', container_slot_type['name']) #インテントの作成 intent = lex.put_intent( name='OrderIntent', #インテント内のスロット slots=[ { 'name': 'Flavor', 'slotConstraint':'Required', 'slotType':'FlavorSlotType', 'slotTypeVersion': '1', 'valueElicitationPrompt': { 'messages': [{ 'contentType': 'PlainText', 'content': 'Vanilla, chocolate or strawberry?' }], 'maxAttempts': 3 } }, { 'name': 'Container', 'slotConstraint': 'Required', 'slotType': 'ContainerSlotType', 'slotTypeVersion': '1', 'valueElicitationPrompt': { 'messages': [{ 'contentType': 'PlainText', 'content': 'Corn or cup?' }], 'maxAttempts': 3 } } ], #発話例 sampleUtterances=[ 'I want {Flavor} ice cream in {Container}', '{Flavor} ice cream {Container}', 'ice create' ], #完了時のセリフ conclusionStatement={ 'messages': [{ 'contentType': 'PlainText', 'content': 'OK, {Flavor} ice cream in {Container}' }], }, #完了の動作 fulfillmentActivity={'type': 'ReturnIntent'}, createVersion=True) print('intent:',intent['name']) #ボットの作成 bot = lex.put_bot( name ='MyBot', locale='en-US', childDirected=False, #インテント intents=[ { 'intentName': 'OrderIntent', 'intentVersion': '1' } ], #中止時のセリフ abortStatement={ 'messages':[ { 'contentType': 'PlainText', 'content': 'Please try again.' } ] }, voiceId='Joanna', createVersion=True) print('bot:', bot['name']) #ボット作成の進捗表示 start = time.time() status = '' while status not in ['READY', 'FAILED']: #ボットを取得 status = lex.get_bot(name='MyBot', versionOrAlias='1')['status'] time.sleep(10) print('{:7.2f} {}'.format(time.time()-start, status)) #作成に失敗した場合は理由を表示 if status == 'FAILED': print(lex.get_bot( name='Mybot', versionOrAlias='1')['failureReason']) #ボットエイリアスを作成 bot_alias = lex.put_bot_alias( name='MyBotAlias', botName='MyBot', botVersion='1') print('bot alias:', bot_alias['name']) 実行しbotを作成します python lex_create_bot.py 実行結果 botが作成されました。筆者は先にロールやスロットを作成し、同じロールやスロットを何度も作成し怒られて時間がかかりました。(ちなみに先にロールやスロットを作してしまった際には作成した部分をコメントアウトしてスキップして下さい) botが作成されたのでbotと文字で会話するプログラムを作成します。 文字でbotと会話する 以下コードになります。 lex_chat_text.py import boto3 import uuid #①lex-runtimeサービスクライアント作成 lex_runtime = boto3.client('lex-runtime', 'us-east-1') #②ユーザーIDの作成 user = str(uuid.uuid1()) #③インテントが完了するまで続ける state = '' while state != 'Fulfilled': result = lex_runtime.post_text( botName='MyBot', botAlias='MyBotAlias', userId=user, inputText=input('You: ')) #ボットの応答の表示 print('Bot:', result['message']) #会話の状態を取得 state = result['dialogState'] #④取得したスロットの値の表示 print() print('Flavor :', result['slots']['Flavor']) print('Container :', result['slots']['Container']) ①lex_runtimeのサービスクライアントを作成します。bot作成ではlexのサービスクライアントを作成しますが、botの会話にはlexRuntimeサービスクライアントを使います。 ②ユーザーIDの作成 uuidを用いてランダムなユーザーIDを作成する。 ③会話の状態がFulfilledになるまで会話を続ける。 post_textメソッドを用いてbotに文字列を送信する。post_textメソッドの戻り値はresultに代入されます。result['message']でbotの応答を指定したりresult['dialogState']で会話の状態を取得します。 ④スロットの値を表示 post_textメソッドの戻り値であるresult内の['Flavor']や['Container']を取得して画面に表示します。 botとの文字での会話の実行 会話ファイルを実行しチョコレートアイスを注文、するとbotがcornかcupか聞いてcornと答えるとbotから最終の注文を答えてくれました。 補足ですがFlavorのみ答えても無視されます(笑) iceというワードが入ると認識してくれましたね、どうやらちゃんとワードを与えないとシカトされる可能性もあるのでお忘れなく。 まとめ 今回はlexを用いて簡単なbot作成とbotとの会話をご紹介しました。 参考文献 この記事は以下の情報を参考にして執筆しました AWSでつくるAIプログラミング入門
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む