20210727のPythonに関する記事は30件です。

Codeforces Global Round 15 B. Running for Gold 最強がいる場合の判定

1-indexedで説明します。 題意(意訳) n 人の人がいる。これらの人は過去に5回の試合に出ており、その順位が与えられる。順位はdistinct(同順はない)である。 チャンピオンとは、3つ以上の試合において、他のどんな人よりも順位が強い(数値としては低い)人のことである。各試合の間に優劣はない(ポイントの高い試合、などはない) チャンピオンがいるならその人を述べよ(複数いるならどれでもいい)。いないならそれを述べよ ※後述の通り、複数の候補が存在することはありません こう考えた 前提 まず、(aがbに)勝つをaはbより順位が強い試合が3つ以上あると定義します。 さて、問題のポイントは、チャンピオンがいるなら、1人に定まるです。なぜなら、チャンピオン$a$がいるとするなら、その人は3つ以上の試合の順位がほかのすべての人より強いです。もしも、他のチャンピオン$b$がいるとすると、その人は$a$に勝てる試合が3つ以上あることになりますが、これは前提に反します。 ただし、サンプルにある通り、チャンピオンがいない場合もあるです。 こう考えた STEP1 チャンピオン候補を決める STEP1では、チャンピオンがいると仮定します。(この仮定で処理を進めるのがとても重要!) まず、人$1$をチャンピオンと仮定して、人$2,3,..,n$と比較します。もし、すべてに勝ったなら、人$1$がチャンピオンです。もし、人$k$に負けたとすると、人$k$がチャンピオンかもしれないので、人$k$を基準にして$人k+1, k+2,..n$と比較します。これを繰り返します。$O(N)$です。 こうして最後に残った(最後まで処理した)チャンピオン候補kは真にチャンピオンとは限りません. $k$よりも前の人に負けることがあるからです。ただし、チャンピオンがいるとするならば、最後に残ったチャンピオン$k$が答えです。なぜなら、前提の通り、チャンピオンはほかの人に負けるはずがないので、どういう順序(例えばソートされても)で上記の施行をしても絶対に最後まで残ります。 こう考えた STEP2 チャンピオンかを確かめる そこで、チャンピオンかを判定します。候補$k$を$k$を除く$1,2,..,n$と愚直に比較します。すべてに勝てればチャンピオンです。$O(N)$です。 サンプル2の通り、途中で負けることがありますが、この場合は-1(チャンピオンはいない)です。他に本当のチャンピオンがいることはありません。STEP1後段の通り、それが存在するなら、$k$はそれに負けています。 実装 結果、$O(N)$で解けました。 私はSTEP1がうまく思い浮かばず、トーナメント形式でSTEP1を処理しました。(2人ずつ取っていき、買ったほうだけ次のリストに残す方法です)。 from copy import deepcopy def do(): n = int(input()) dat = [] for i in range(n): l = map(int, input().split()) l = list(l) l.append(i) dat.append(l) odat = deepcopy(dat) # STEP1: while len(dat) > 1: newdat = [] for i in range(0, len(dat), 2): ls = dat[i:i+2] if len(ls) == 1: newdat.append(ls[0]) continue a = ls[0] b = ls[1] cnt = 0 for i in range(5): if a[i] < b[i]: cnt += 1 if cnt >= 3: newdat.append(ls[0]) else: newdat.append(ls[1]) dat = newdat # STEP2 candidate = dat[0] for x in odat: cnt = 0 for i in range(5): if candidate[i] <= x[i]: cnt += 1 if cnt >= 3: continue print("-1") return print(candidate[5] + 1) q = int(input()) for _ in range(q): do()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Progateで作ったWebアプリをDjangoで作ってみる2! Part1 -初期設定編-

目標物の確認 ProgateのNode.jsコースで作ったブログアプリと同じものをDjangoで作ってみます。 Djangoでのアプリ開発の一連の流れを整理するために記していきます。 完成イメージ 初期設定 まずは初期設定です。 プロジェクトを作成して、アプリ(blogapp)を作成します。 > django-admin startproject blogapp > cd blogapp > python manage.py startapp blog 「templates」ディレクトリと「static」ディレクトリを作成して、さらにそれぞれの下層に「blog」ディレクトリを作成します(☆1)。 「blog」ディレクトリ構造は、今の段階でこんな感じです。 blog ├── __init__.py ├── admin.py ├── apps.py ├── migrations │   └── __init__.py ├── models.py ├── static # ☆1 │   └── blog # ☆1 ├── templates # ☆1 │   └── blog # ☆1 ├── tests.py ├── urls.py └── views.py setting.pyのINSTALLED_APPSにアプリ名(blog)を登録します(☆2)。 blogapp/blogapp/setting.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', #☆2 ] プロジェクトのurls.pyファイルからアプリのurls.pyファイルを呼び出すためのコードを書きます(☆3)。 blogapp/blogapp/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('blog.urls')) #☆3 ] トップ画面を作るところまでやっていきます。アプリのurls.pyにコードを追加します。 namespace(app_name)も設定しておきます。 blogapp/blog/urls.py from django.urls import path from .views import BlogTop app_name = 'blog' urlpatterns = [ path('', BlogTop.as_view(), name='top'), ] トップページの表示はTemplateViewで対応します(☆4)。 blogapp/blog/views.py from django.shortcuts import render from django.views.generic import TemplateView class BlogTop(TemplateView): #☆4 # top.htmlをレンダリング template_name = 'blog/top.html' トップページはDjangoのタグで整えます(☆5)。 top.html {% load static %} <!--☆5--> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>BLOG</title> <link rel="stylesheet" href="{% static 'blog/style.css' %}"> <!--☆5--> </head> <body> <div class="top"> <div class="wrapper"> <div class="content"> <h2>わんこの学びブログ</h2> <h1><img src="{% static 'blog/top-logo.svg' %}" alt=""></h1> <!--☆5--> <p>プログラミングに関する雑学ブログ。<br>だれかに教えたくなる豆知識をお届けします。</p> <a class="btn" href="{% url 'blog:list' %}">読みはじめる</a> <!--☆5--> </div> <img class="image" src="{% static 'blog/top.svg' %}" alt=""> <!--☆5--> </div> </div> </body> </html> models.pyはまだ作成していませんが、Djangoがデフォルトで準備しているUserテーブルを作成するため、マイグレーションしておきます。 > python manage.py makemigrations > python manage.py migrate これで初期設定は完了しました。 トップページも表示されました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Progateで作ったWebアプリをDjangoで作ってみる2! Part1 --初期設定編--

目標物の確認 ProgateのNode.jsコースで作ったブログアプリと同じものをDjangoで作ってみます。 Djangoでのアプリ開発の一連の流れを整理するために記していきます。 完成イメージ 初期設定 まずは初期設定です。 プロジェクトを作成して、アプリ(blogapp)を作成します。 > django-admin startproject blogapp > cd blogapp > python manage.py startapp blog 「templates」ディレクトリと「static」ディレクトリを作成して、さらにそれぞれの下層に「blog」ディレクトリを作成します(☆1)。 「blog」ディレクトリ構造は、今の段階でこんな感じです。 blog ├── __init__.py ├── admin.py ├── apps.py ├── migrations │   └── __init__.py ├── models.py ├── static # ☆1 │   └── blog # ☆1 ├── templates # ☆1 │   └── blog # ☆1 ├── tests.py ├── urls.py └── views.py setting.pyのINSTALLED_APPSにアプリ名(blog)を登録します(☆2)。 blogapp/blogapp/setting.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', #☆2 ] プロジェクトのurls.pyファイルからアプリのurls.pyファイルを呼び出すためのコードを書きます(☆3)。 blogapp/blogapp/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('blog.urls')) #☆3 ] トップ画面を作るところまでやっていきます。アプリのurls.pyにコードを追加します。 namespace(app_name)も設定しておきます。 blogapp/blog/urls.py from django.urls import path from .views import BlogTop app_name = 'blog' urlpatterns = [ path('', BlogTop.as_view(), name='top'), ] TemplateViewでトップページを表示します(☆4)。 blogapp/blog/views.py from django.shortcuts import render from django.views.generic import TemplateView class BlogTop(TemplateView): #☆4 # top.htmlをレンダリング template_name = 'blog/top.html' トップページはDjangoのタグで整えます(☆5)。 top.html {% load static %} <!--☆5--> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>BLOG</title> <link rel="stylesheet" href="{% static 'blog/style.css' %}"> <!--☆5--> </head> <body> <div class="top"> <div class="wrapper"> <div class="content"> <h2>わんこの学びブログ</h2> <h1><img src="{% static 'blog/top-logo.svg' %}" alt=""></h1> <!--☆5--> <p>プログラミングに関する雑学ブログ。<br>だれかに教えたくなる豆知識をお届けします。</p> <a class="btn" href="{% url 'blog:list' %}">読みはじめる</a> <!--☆5--> </div> <img class="image" src="{% static 'blog/top.svg' %}" alt=""> <!--☆5--> </div> </div> </body> </html> models.pyはまだ作成していませんが、Djangoがデフォルトで準備しているUserテーブルを作成するため、マイグレーションしておきます。 > python manage.py makemigrations > python manage.py migrate これで初期設定は完了しました。 トップページも表示されました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

車のメンテナンス時期を通知。

私は車が好きで、メンテナンス等、できれば自分でやりたい派です。 でも、交換時期を覚えていられないんです、、 「前いつエンジンオイル変えたっけ?」的な感じです。 また、趣味でPythonをいじっていまして、文法をやや覚えたくらいのレベルではありますが、何かに応用できないかと考えていました。 そんな時に出会ったのがこのページ す、すげぇ、、 趣味の融合、楽しそう、、 そこで、Pythonを使ってできるようにしてみました! と言っても、先人たちの偉大なるレガシーに少し手を加えただけです。 以下が参照させていただいたページです。 以下がコードです。独学で、こういった実践的なものは初めて作りました。 改善点ありましたら、是非教えてください。 また、コードの下に<今後こうしていきたい>がありますので、 書き方のヒント等、こちらもぜひ教えてください。 GPS.py import location # このモジュールで、GPSを取得します。 from datetime import datetime import threading import time import queue import csv from geopy.distance import geodesic import numpy as np import requests # 使用リスト一覧 g_locs = [] ido = [] keido = [] distance = [] awasete = [] # スレッド動作する、GPS取得関数 def GPS(que,locs): print('GPS thread has started.') # 定期的なGPS取得スタート location.start_updates() while True: #もし何か文字列を入力されたら、終了。 if not que.empty(): break # 緯度&経度取得 loc = location.get_location() lat = loc["latitude"] # 緯度 lng = loc["longitude"] # 経度 #各リストに格納 ido.append(lat) keido.append(lng) # 現在時刻取得 now = datetime.now() print(now, lat, lng) # 1秒待機 time.sleep(1) # ループを抜けたら、定期的なGPS取得を終了。 location.stop_updates() # キューのインスタンス生成 que = queue.Queue() # スレッドでGPS関数開始 th1 = threading.Thread(target=GPS,args=(que,g_locs,)) th1.start() # もしユーザーから文字列入力を受け取ったら、キューに送りスレッド終了。 while True: inp = input() que.put(inp) if not inp == '': break th1.join #リストから緯度経度iと次の緯度経度i+1取得 for i in range(len(ido)-1): a = (ido[i], keido[i]) b = (ido[i+1], keido[i+1]) #距離に換算し、リストに格納 dis = geodesic(a, b).km distance.append(dis) #各点の距離を合計=走行距離 this_trip = sum(distance) easy = round(this_trip) print("You drove" + str(easy)+"km") #走行距離を.csvに上書き保存 file = open('Moving_distance.csv', 'a') file.write(str(easy)+"\n") file.close() #もう一回ファイルを開いて、これまでの走行距離を合計 with open("Moving_distance.csv", "r") as p: k = str(p.read()).replace("\n", "") awasete.append(int(k)) goukei = sum(awasete) #合計が5000kmを超えたら通知して0にリセット if goukei > 5000: def main(): send_line_notify('5000km、エンジンオイル交換時期です') def send_line_notify(notification_message): line_notify_token = '自分で取得したキー' line_notify_api = 'https://notify-api.line.me/api/notify' headers = {'Authorization': f'Bearer {line_notify_token}'} data = {'message': f'message: {notification_message}'} requests.post(line_notify_api, headers = headers, data = data) if __name__ == "__main__": main() file = open('Moving_distance.csv', 'w') file.write(str(0)) file.close() <iOSショートカットと連携> Pythonistaの右上、スパナのマークから、”shortcuts” → “Pythonista URL”と進むと、パスを取得できる。 それをショートカットのオートメーションにある、Bluetoothをトリガーにしたものに入れれば、自動で処理スタートは簡単! 終わらせるためには目的地到着後、何らかの手動入力しないといけない、、 (ショートカットが、Pythonistaの「引数を渡す」に対応してない?) <今後こうしていきたい> ・エンジンオイルだけじゃなく、他のオイルやバッテリー等も管理したい。   →.csvじゃなくて.xlsxでsheetを分けて管理すべき? ・.csvに走行距離だけじゃなくて日時も入れたい。   → k = str(p.read()).replace("\n", "")のところで書き込んだ日時も消す必要があるけど、消し方がわからない、、 ・バックグラウンドでのGPS受信はどうやらできなそうなので、ケータイがスリープしない様にする工夫が必要。 ・CarPlayの接続が切れたら自動で処理がストップできるようにしたら完璧。   → これはどうしたらいいか見当もつかない。ヒント・アイディアお願いします!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

numpy.piecewiseとmatplotlibで区分関数を描画する

はじめに 最近高校物理の復習をしていて、折角だからグラフを描く問題をmatplotlibで解いてみようと思ったんですが、一次関数のxの値で傾きが変わるやつを描くいいやり方がわからない。通る点をハードコーディングしてplt.plotに渡すという方法はすぐ見つかりましたが、できれば関数は関数として記述したい。しかし調べようと思っても「一次関数のxの値で傾きが変わるやつ」の呼び方がわからないから全然正解にたどり着けない。 といった具合に暗中模索していたんですが、小一時間かかってようやく「区分関数」という名称がわかり、numpy.piecewiseというソリューションに行き着くことができました。 私のように「区分関数」なんて言葉を知らない新米文系エンジニアのためにも、記事として残しておこうと思います。 やりたいこと 0 ≦ x ≦ 5 の範囲で、以下の区分関数のグラフを書きたい(xは0.1刻みとする)。 y = \begin{cases} 3x + 4 \quad(x ≦ 2) \\ 10\hspace{29pt}(2 < x) \\ \end{cases} numpy.piecewise(x, condlist, funclist) 区分関数(piecewise-defined function)を評価する関数。 x: 定義域。ドキュメントにはndarrayかscalarと書いてありますが、どんな時にscalarを使うかはわかりません。普通はarangeとかlinspaceで生成したndarrayを渡すんだと思います。 condlist: 条件文のリスト funclist: 各条件文がTrueの時の関数のリスト 戻り値: xがndarrayの場合、xの各要素に指定の関数を適用したndarrayが返る。 コード 何も指定しないとy軸が切片の4からになってしまうので、原点を表示させるためにylimで表示範囲を指定しています。 import numpy as np import matplotlib.pyplot as plt x = np.arange(0, 5, 0.1) y = np.piecewise(x, [x <= 2, x > 2], [lambda x: x * 3 + 4, 10]) plt.plot(x, y) plt.ylim(0, 12) plt.xlabel('x') plt.ylabel('y') plt.show() 実行結果 Colaboratoryで実行し、想定通りのグラフが表示されました。 感想 やっぱり関数は関数のまま記述できたほうがすっきりしますね。 matplotlib初心者ですが、こんなところで躓くとは思っていませんでした。心が折れる前に解法が見つかってよかったです。 最後までご覧いただきありがとうございました。 参考文献 numpy.piecewise — NumPy v1.21 Manual
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Mac] Blender上のPythonで特定のファイルを再帰的にインポート(オプション指定あり)

備忘録 前提 mac OS Big Sur 11.2.3 Blender2.93.0 あるディレクトリ内の全ての.objファイルをインポートしたい import os import bpy # 該当ディレクトリの絶対パス search_dir = bpy.path.abspath('/Path/to/the/Dir') ext_list = ["obj"] # ⇦ 他の拡張子をインポートしたい場合,ここを変更 # 該当ファイルをリストアップ obj_list = [] for root, dirs, files in os.walk(search_dir): for ext in ext_list: obj_list.extend([os.path.join(root, file) for file in files if ext in file]) # ひとつずつインポート.OBJファイルの場合 for item in obj_list: bpy.ops.import_scene.obj(filepath = item) インポートのオプションを変更したい GUIでモデルをインポートする場合と同様に,オプションを指定してインポートすることも可能です.下はOBJファイルをインポートする場合に指定可能な引数です. インポートしたいモデルの拡張子によって関数及び引数は異なるため,Import Scene Operators - Blender Python API等を参考に調べましょう. OBJファイルの場合のデフォルトの引数 bpy.ops.import_scene.obj(filepath='', filter_glob='*.obj;*.mtl', use_edges=True, use_smooth_groups=True, use_split_objects=True, use_split_groups=False, use_groups_as_vgroups=False, use_image_search=True, split_mode='ON', global_clamp_size=0.0, axis_forward='-Z', axis_up='Y') ↓はY軸を前方向に,Z軸を上にするべく引数を変更した場合です. bpy.ops.import_scene.obj(filepath = item, axis_forward='Y', axis_up='Z') エラーが出たとき Pythonスクリプトが失敗。システムコンソールのメッセージをチェックしてください というエラーが出ることがあります. Mac PCでLaunchpad等のボタン操作でBlenderを起動した場合,エラーメッセージを確認することができません. ターミナル上から下のようにBlenderを起動するとコンソールが別ウィンドウで立ち上がり,エラーメッセージを見ることができます. # 一例 open /Applications/Blender.app/Contents/MacOS/Blender この記事は以下を参考に作成しました. [自分用メモ] Blender Python を使ったバッチ処理まとめ Blender Pythonで複数メッシュをOBJ形式で一括エクスポートする。 指定した複数の拡張子のファイルを再帰的にすべて検索
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

不意なPythonのアップデートでプログラムが動かなくなってしまった話

前提 私は初学者なのでPythonについては全く詳しく有りません。 完全な個人での備忘録になります Python3.8.?➡3.9.6へのアップデートで今回の問題が起きました。 Pythonをピンポイントでアップデートしたかったわけでなく、brew upgradeで特に意識せずにアップデートしてしまいました。 現在は先頭行で文字コードを指定はしなくても動くため、この項目については一時的な端末の不具合の可能性が高いです。 コメント頂きありがとうございます(^o^) 参考: homebrew : Python3 をアンインストールして pyenv からインストールし直す Python Package Index 一番ラクな解決法 Pythonモジュールのimportができなくなったときの対処記録 上記に記載のあるようにインストール済みモジュールのパスをPythonシェルで追加してあげることで解決するようです。 インストール済みモジュールのパス確認 pip show インストール済みモジュール名 Python3シェル >>> import sys >>> sys.path.append('該当のパス') 何が起きたのか 下記2つを実行したら勉強途中のPython3のプログラムが動かなくなった brew update brew upgarde 主な症状として下記2つ ・SyntaxError: Non-ASCII character '\xe3' in fileというエラー ・ImportError: No module named requests(今まで使えていたモジュール)というエラー SyntaxError: Non-ASCII character '\xe3' in fileというエラーについてはPython3のプログラム先頭で文字コードを指定することで解決できた。 ただし、本来Python3では文字コードの指定は必要なかったはずだと思ってましたがどうしてでしょう?? 何故今になってこのエラーが出たのかが分かりません。またPython2を使用したことがないのでよく分かりません。 先頭行で文字コードを指定する # -*- coding: utf-8 -*- 例 # -*- coding: utf-8 -*- import requests from bs4 import BeautifulSoup import ... ... 2つめImportError: No module named requests(今まで使えていたモジュール)というエラーについては、どうしてか今まで使用できていたPythonモジュールが使用できなくなりました。 おおよその理由は掴めていて、pipでのモジュールインストール先が前のバージョン3.8のディレクトリのため3.9のパスとして認識されていませんでした。 echo "pipインストールリスト" pip list echo "requestsモジュールの詳細表示" pip show requests まず関係ないかもしれませんがpipとpip3の違いが分かっておらず、今まで使用していたモジュールは全てpipでインストールしていました。 pipには下記表のような対応バージョンがあるようです。 対応表 Python2.7 Python3 pip ○ ○ pip3 ✗ ○ どう解決したか 結論から言うと何が正解なのかわからず、既存の環境を捨て公式HPから.pkgで新たに編集時(2021/07/27)、最新のPython3.9.6をインストールしました。 手順 ・pip で既存モジュールのアンインストール ・公式サイト Python.org からのインストール(Mac) ・pip3 で再度モジュールをインストール pip で既存モジュールのアンインストール 現在入ってるモジュールをfreezeで書きだし、テキストファイルに保存 それをuninstallで読み込む ターミナルに入力 pip freeze > piplist.txt sudo pip uninstall -r piplist.txt 公式サイト Python.org からのインストール(Mac) Download Python からインストーラをダウンロードし,インストールします。/Library/Frameworks/Python.framework/Versions/3.9/bin に python3 などのバイナリが入ります。 インストール後改めてパスを確認するとパスだけでなくその他諸々変更されていると思います。 pip3 で再度モジュールをインストール pip3 install モジュール名1 モジュール名2 モジュール名3 のようにまとめてインストールできるようです。 もしくわpip モジュールアンインストールの際に書き出したpiplist.txtで入っていたモジュールを一括でインストールする、という方法もあります。 ターミナル sudo pip3 -r piplist.txt beautifulsoupをインストールする際は pip install beautifulsoup4 のように明示してあげるほうが良いようです。 参考:beautifulsoup4 4.9.3 最後に 私はこれで満足していますが正直言ってただの先延ばしになっているだけなので、どうにか正解を見つけたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Processing: text()の日本語入力が文字化けする問題を超簡単に解決する方法

はじめに 今回はProcessingを使う人が困りがちな、text()で日本語入力しようとした際に文字化けする問題を最も簡単に解決できる方法(筆者体感)を紹介します。 筆者はPythonで説明します。 ※Javaでの動作は確認できていないのでもし確認できた方はコメントで教えてください! 本題 文字化け みなさん、Processingにて下記のようにtext() を使って日本語を表示しようとしたことはありませんか? def setup(): size(400, 400) background(0) font = createFont("Meiryo", 50) textFont(font) textSize(50) def draw(): fill(255, 0, 0) textAlign(CENTER, CENTER) text("文字化け嫌い", width/2, height/2) 一見なんの問題もないコードなのに実行結果は。。。 こうなってしまいました。。。 createFont() とtextFont() でちゃんとフォントも指定してるのに。。。って思いますよね でもこれ以下のような、ワンステップですぐに解決できます。 解決方法 その簡単な方法とは ""の前にuを打ち込むだけ! 以下の通りです text(u"#入力したい文字列", x座標, y座標) 先程の全体のコードの場合はこんな感じ def setup(): size(600, 400) background(0) background(0) font = createFont("Meiryo", 50) textSize(50) def draw(): fill(255, 0, 0) textAlign(CENTER, CENTER) text(u"文字化け嫌い", width/2, height/2) 実行結果は以下の通りです。 以上になります。 この記事がProcessingのtext() で困ってる誰かさんの役に立てればと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】個人用LeetCode集

二分探索 リスト内の目的の値を$O(\log(n))$で探索するアルゴリズム def binary_search(li, x): lo, hi = 0, len(li) while lo < hi: mid = (lo+hi)//2 if li[mid] < x: lo = mid+1 else: hi = mid return lo 戻り値: liの中でx以上最小の値のインデックス liは既にソート済み xがliの最大値より大きいときlen(li)を返す ダイクストラ法 各頂点までの距離を$O((v+e)\log(v))$で求めるアルゴリズム from heapq import heappop, heappush def dijkstra(edge_list, s): n = len(edge_list) todo = [] heappush(todo, (0, s)) dist_list = [float('inf')]*n dist_list[s] = 0 while todo: dist, node = heappop(todo) for to, cost in edges_list[node]: if dist_list[to] > cost + dist: dist_list[to] = cost + dist heappush(todo, (cost + dist, to)) return dist_list edge_listは各頂点ごとに(辺、距離)の集合を格納したリスト 第二引数は始点 戻り値は各頂点までの距離のリスト UnionFind木 グループ分けを効率的に処理するための木構造 class UnionFind: def __init__(self, n): self.parents = [-1] * n def root(self, x): todo = [] while self.parents[x] >= 0: todo.append(x) x = self.parents[x] for y in todo: self.parents[y] = x return x def find(self, x, y): return self.root(x) == self.root(y) def unite(self, x, y): r1 = self.root(x) r2 = self.root(y) if r1 == r2: return p1 = self.parents[r1] p2 = self.parents[r2] if p1 <= p2: self.parents[r2] = r1 if p1 == p2: self.parents[r1] -= 1 else: self.parents[r1] = r2 self.parentsの要素は非負なら親のインデックス、負ならランクを格納 セグメント木 区間に対する操作を$O(\log n)$で行えるデータ構造 class SegTree: def __init__(self, init_list): n = len(init_list) self.num = 1 << (n-1).bit_length() self.tree = [0]*(2*self.num-1) for i in range(n): self.update(i, init_list[i]) def update(self, k, x): k += self.num self.data_list[k] += x while k > 1: self.tree[k >> 1] = self.tree[k] + self.tree[k ^ 1] k >>= 1 def query(self, l, r): total = 0 l += self.num r += self.num while l < r: if l & 1: total += self.tree[l] l += 1 if r & 1: total += self.tree[r] l >>= 1 r >>= 1 return total 区間和だけでなく、最大値を求めるのにも有効。応用範囲が広い 区間の値の変更がない場合は累積和の方が効率が良い 補足 公式リファレンスや蟻本に掲載されている実装例や、AtCoderのコードなどを参考にさせて頂きました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

超初心者がPythonで移動平均線をPlotlyを使ってグラフ化してみた(5分足チャートと同時に表示)

移動平均線もPlotlyで表示してみたい 参考 以下のコードに少し足すだけ コード draw_with_plotly.py import pandas as pd import plotly.graph_objects as go import datetime # 1分足データを取得 df = pd.read_csv('./temp_historical_data/USDJPY.csv', nrows=1500) df.columns = ["Date", "Open", "High", "Low", "Close", "Volume"] df["Date"] = pd.to_datetime(df["Date"]) df.set_index("Date", inplace=True) # 5分足に変換 df5 = pd.DataFrame() rule = '5T' df5['Open'] = df.Open.resample(rule).first() df5['Close'] = df.Close.resample(rule).last() df5['High'] = df.High.resample(rule).max() df5['Low'] = df.Low.resample(rule).min() # SMA計算 df5["SMA5"] = df5["Close"].rolling(window=5).mean() df5["SMA20"] = df5["Close"].rolling(window=20).mean() # グラフ化 interval = 12 vals = [] labels = [] for i in range(len(df5)//interval): vals.append(df5.index[i*interval]) labels.append(df5.index[i*interval].strftime('%Y-%m-%d %H:%M')) fig = go.Figure( data=[ go.Candlestick( x=df5.index, open=df5['Open'], high=df5['High'], low=df5['Low'], close=df5['Close'], hovertext=['date:{}<br>open:{}<br>high:{}<br>low:{}<br>close:{}' .format(i.strftime('%Y-%m-%d %H:%M'), df5.loc[i,'Open'],df5.loc[i,'High'],df5.loc[i,'Low'],df5.loc[i,'Close']) for i in df5.index], hoverinfo="text" ), go.Scatter(x=df5.index, y=df5.SMA5, line=dict(color='orange', width=1)), go.Scatter(x=df5.index, y=df5.SMA20, line=dict(color='green', width=1)) ], layout = go.Layout( xaxis = dict( ticktext = labels, tickvals = vals, tickangle = -90 ), ) ) fig.show() グラフ 感想 簡単。ボリンジャーバンドの同時表示も同じ要領でできそうなのでそちらは割愛しようかな。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

超初心者がPythonで5分足チャートをPlotlyを使ってグラフ化してみた

Plotly使った方が見やすいグラフができそう?と思ったのでやってみた。 参考 1分足チャートのデータの取得方法は以下 1分足チャートの5分足への変換は以下 コード draw_candlestick_chart_with_plotly.py import pandas as pd import plotly.graph_objects as go import datetime # 1分足データを取得 df = pd.read_csv('./temp_historical_data/USDJPY.csv', nrows=1500) df.columns = ["Date", "Open", "High", "Low", "Close", "Volume"] df["Date"] = pd.to_datetime(df["Date"]) df.set_index("Date", inplace=True) # 5分足に変換 df5 = pd.DataFrame() rule = '5T' df5['Open'] = df.Open.resample(rule).first() df5['Close'] = df.Close.resample(rule).last() df5['High'] = df.High.resample(rule).max() df5['Low'] = df.Low.resample(rule).min() # グラフ化 interval = 12 vals = [] labels = [] for i in range(len(df5)//interval): vals.append(df5.index[i*interval]) labels.append(df5.index[i*interval].strftime('%Y-%m-%d %H:%M')) print(labels) fig = go.Figure( data=go.Candlestick( x=df5.index, open=df5['Open'], high=df5['High'], low=df5['Low'], close=df5['Close'], hovertext=['date:{}<br>open:{}<br>high:{}<br>low:{}<br>close:{}' .format(i.strftime('%Y-%m-%d %H:%M'), df5.loc[i,'Open'],df5.loc[i,'High'],df5.loc[i,'Low'],df5.loc[i,'Close']) for i in df5.index], hoverinfo="text" ), layout = go.Layout( xaxis = dict( ticktext = labels, tickvals = vals, tickangle = -90 ), ) ) fig.show() グラフ 感想 Plotlyの方がお手軽に見やすいグラフが作れそう。SMAとか同時に描画してみたい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Djangoの旅 ~Part13~ bootstrap4導入編

目標 bootstrap4導入 bootstrap4とは DBなどの登録/修正のフォーム系HTMLを見やすくするのに使用するのが django-bootstrap4というPythonモジュール!!!!! 一覧を表示したりするようなHTMLは、普通のBootstrapを使用してデザインをよくするのがいい 流れ 1・モジュールインストール 2・settings.pyに追加 コード解説 $ pip install django-bootstrap4 1・モジュールインストール もちろんDjangoが入っている環境を起動してインストール APP/APP/settings.py INSTALLED_APPS = [ 'testapp.apps.TestappConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'bootstrap4',#追加 ] 2・settings.pyに追加 'bootstrap4'を記述
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プログラミング初心者によるPython3 エンジニア認定基礎試験 合格記

■Python3 エンジニア認定基礎試験 概要 pythonの基本的な文法を問う試験 ■筆者の試験結果 以下に記載する勉強法で925点/1000点を取ることができました。 勉強方法に悩んでいる方は以下の勉強方法を参考にしてみてください。 ■受験前の筆者情報 ○ech AcademyのPythonコースを受けていたので勉強時間はだいぶ短くなったと思います。 上記以外はまるっきりの素人です。 ※オンラインスクールに通わないといけないわけではないので誤解の無い様お願いします。 実務経験 無し 資格 無し 勉強状況 Tech ○cademy Pythonコース終了済み(2021/6月初旬) ■勉強方法全体像&時間 順番 勉強内容     勉強方法 所要時間 1 Pythonの基礎を学ぶ プログラミング超初心者が初心者になるためのPython入門(1)~(3)を読む   12時間/3冊 2 自分の理解度を把握 DIVE INTO EXAMで模擬試験を解く 3時間 3 試験範囲を理解 Python チュートリアルを読む 32時間 4 模擬試験を解いて理解度向上 DIVE INTO EXAM・PRIME STUDYを解く/PYTHON チュートリアルで間違えた問題を理解する 32時間 ■勉強方法詳細 いきなりPython チュートリアルを勉強するのはやめましょう。 ∵基礎知識がない状態で理解しようとすると非常に難しいため。 1. Pythonの基礎を学ぶ 私はプログラミング超初心者が初心者になるためのPython入門(1)~(3)を読んで基礎を確認しました。 AmazonのKindle unlimitedで無料で読めるのでおすすめです。 2. 自分の理解度を把握 DIVE INTO EXAMで模擬試験を解いて、今の自分の実力を確認しました。 ここでぼろぼろな結果を見て、自分の尻に火をつけました。 3. 試験範囲を理解 ここでついにPython チュートリアルに手を付けます。 Python チュートリアルを上から順に読んでいきます。 私は下記のようにこのチュートリアルを読みました。 どう勉強をすすめるか迷っている方は参考にしてください。 1回目 ・ざっと読んでどこにどんなことが書いてあるのかを確認しながら、覚えれるところは覚える(ここで本気でわからないところを全部理解しようとすると時間がいくらあっても足りない) ・Evernoteを使って各章をまるごとノートにコピーする(後の模擬試験で間違えた際の勉強時にハイライトをつけたりすることができる) 2回目以降 模擬試験で間違えた問題の箇所をEvernoteにまとめる 4. 模擬試験を解いて理解度向上 DIVE INTO EXAM・PRIME STUDYで模擬試験を解き、間違えた問題をPython チュートリアルの対象箇所を読んで理解しました。 ■勉強の進め方で参考にしたサイト https://qiita.com/gomasa/items/a0c91cea24f41fd63c1c https://ccie-go.com/python-exam-study/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プログラミング初心者によるPython3 エンジニア認定基礎試験 勉強方法

■Python3 エンジニア認定基礎試験 概要 pythonの基本的な文法を問う試験 ■筆者の試験結果 以下に記載する勉強法で925点/1000点を取ることができました。 勉強方法に悩んでいる方は以下の勉強方法を参考にしてみてください。 ■受験前の筆者情報 ○ech AcademyのPythonコースを受けていたので勉強時間はだいぶ短くなったと思います。 上記以外はまるっきりの素人です。 ※オンラインスクールに通わないといけないわけではないので誤解の無い様お願いします。 実務経験 無し 資格 無し 勉強状況 Tech ○cademy Pythonコース終了済み(2021/6月初旬) ■勉強方法全体像&時間 順番 勉強内容     勉強方法 所要時間 1 Pythonの基礎を学ぶ プログラミング超初心者が初心者になるためのPython入門(1)~(3)を読む   12時間/3冊 2 自分の理解度を把握 DIVE INTO EXAMで模擬試験を解く 3時間 3 試験範囲を理解 Python チュートリアルを読む 32時間 4 模擬試験を解いて理解度向上 DIVE INTO EXAM・PRIME STUDYを解く/PYTHON チュートリアルで間違えた問題を理解する 32時間 ■勉強方法詳細 いきなりPython チュートリアルを勉強するのはやめましょう。 ∵基礎知識がない状態で理解しようとすると非常に難しいため。 1. Pythonの基礎を学ぶ 私はプログラミング超初心者が初心者になるためのPython入門(1)~(3)を読んで基礎を確認しました。 AmazonのKindle unlimitedで無料で読めるのでおすすめです。 2. 自分の理解度を把握 DIVE INTO EXAMで模擬試験を解いて、今の自分の実力を確認しました。 ここでぼろぼろな結果を見て、自分の尻に火をつけました。 3. 試験範囲を理解 ここでついにPython チュートリアルに手を付けます。 Python チュートリアルを上から順に読んでいきます。 私は下記のようにこのチュートリアルを読みました。 どう勉強をすすめるか迷っている方は参考にしてください。 1回目 ・ざっと読んでどこにどんなことが書いてあるのかを確認しながら、覚えれるところは覚える(ここで本気でわからないところを全部理解しようとすると時間がいくらあっても足りない) ・Evernoteを使って各章をまるごとノートにコピーする(後の模擬試験で間違えた際の勉強時にハイライトをつけたりすることができる) 2回目以降 模擬試験で間違えた問題の箇所をEvernoteにまとめる 4. 模擬試験を解いて理解度向上 DIVE INTO EXAM・PRIME STUDYで模擬試験を解き、間違えた問題をPython チュートリアルの対象箇所をEvernoteにまとめて理解しました。 ノートにまとめるのは理解向上に非常に役に立ったと思っているので皆さんも是非実施してみてください。 ■勉強の進め方で参考にしたサイト https://qiita.com/gomasa/items/a0c91cea24f41fd63c1c https://ccie-go.com/python-exam-study/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Djangoの旅 ~Part11~ フォーム編

目標 フォームの理解 今回は検索した文字を含んでいるデータを表示すること目的とする!!! フォームを利用する時の流れ 1・forms.py・・・フォームクラスで、データ形式を定義 ↓ 2・views.py・・・フォームから受け取ったデータを処理し、テンプレートに渡す ↓ 3・index.html・・・フォームを表示する 流れ 1・forms.py記述 2・views.pyにフォームオブジェクト追加 3・テンプレートにフォーム追加 コード解説 APP/testapp/forms.py from django import forms class SerachForm(forms.Form): keyword = forms.CharField(label='検索',max_length=100) 1・forms.py記述 フォームを定義するのはforms.pyというファイルが必要になるが、このファイルは自動生成されないため 開発者側で自作しなければならない。 作成場所はDjangoアプリケーションディレクトリ 直下 フォームの定義は django.forms.Formを継承する必要がある!!!! APP/testapp/views.py from django.shortcuts import render, get_object_or_404 from django.http import HttpResponse from .models import LikeFood from .forms import SerachForm #追加 # Create your views here. def index(request): serachForm = SerachForm(request.GET)#追加 if serachForm.is_valid():#追加 keyword = serachForm.cleaned_data['keyword']#追加 foods = LikeFood.objects.filter(content__contains=keyword)#追加 else: foods = LikeFood.objects.all() context = { 'foods':foods, 'searchForm':serachForm#追加 } return render(request,'testapp/index.html',context) 2・views.pyにフォームオブジェクト追加 from .forms import SerachForm・・・forms.pyに記述したクラスを読み込み serachForm = SerachForm(request.GET)・・・Getメソッドのrequestで得た情報を用いインスタンス化 ざっとしたPOSTとGETの違い HTTP GET:指定したURLの内容をWebサーバから取り出す(GET) HTTP POST:クライアントが入力した内容をWebサーバに送る(POST) 引用:以下URL https://medium-company.com/http-get-post-%E9%81%95%E3%81%84/ serachForm.is_valid()・・・Form インスタンスで、 is_valid()メソッドを呼び出す時は検証を実行し、データが有効かどうかを示すブール値を返す。 serachForm.cleaned_data['keyword']・・・ バリデーションを通過してきた入力値が格納されている辞書データからkeywordの入力値を取り出している。 バリデーションとは・・・ 入力されたデータが、あるいはプログラミング言語やマークアップ言語の記述が、規定された文法に即して、または要求された仕様にそって、適切に記述されているかどうかを検証することである。 LikeFood.objects.filter(content_contains=keyword)・・・filterメソッドをしようして限定している。 <フィールド名>_<検索条件>==< 値>で絞り込み可能 検索条件はgt(greater than)などあるので調べる必要あり。 APP/testapp/templates/testapp/index.html {% extends './base.html' %} {% block content %} {% if searchForm %}<!--追加--> <form action='{% url "testapp:index" %}' method='get'><!--追加--> <div class='form-group'><!--追加--> {{searchForm}}<!--追加--> <input type="submit" class="btn btn-outline-primary" value="OK" /><!--追加--> <a href="{% url 'testapp:index' %}" class="btn btn-outline-secondary">クリア</a><!--追加--> </div><!--追加--> </form><!--追加--> {% endif %}<!--追加--> <table class='table table-striped table-hover'> {% for food in foods %} <tr> <td>{{ food.content }}</td> <td>{{ food.color }}</td> <td><a href='{% url "testapp:detail" food.id %}' class='btn btn-outline-primary'>詳細</a></td> <td><a href='{% url "testapp:delete" food.id %}' class='btn btn-outline-secondary'>削除</a></td> </tr> {% endfor %} </table> <a href='{%url "testapp:create" %}'>新規</a> {% endblock %} 3・テンプレートにフォーム追加 これで検索した文字を含んでいるデータを表示することはできた!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Djangoの旅 ~Part12~ フォーム編

目標 フォームの理解 今回は検索した文字を含んでいるデータを表示すること目的とする!!! フォームを利用する時の流れ 1・forms.py・・・フォームクラスで、データ形式を定義 ↓ 2・views.py・・・フォームから受け取ったデータを処理し、テンプレートに渡す ↓ 3・index.html・・・フォームを表示する 流れ 1・forms.py記述 2・views.pyにフォームオブジェクト追加 3・テンプレートにフォーム追加 コード解説 APP/testapp/forms.py from django import forms class SerachForm(forms.Form): keyword = forms.CharField(label='検索',max_length=100) 1・forms.py記述 フォームを定義するのはforms.pyというファイルが必要になるが、このファイルは自動生成されないため 開発者側で自作しなければならない。 作成場所はDjangoアプリケーションディレクトリ 直下 フォームの定義は django.forms.Formかforms.ModelFormを継承する必要がある!!!! APP/testapp/views.py from django.shortcuts import render, get_object_or_404 from django.http import HttpResponse from .models import LikeFood from .forms import SerachForm #追加 # Create your views here. def index(request): serachForm = SerachForm(request.GET)#追加 if serachForm.is_valid():#追加 keyword = serachForm.cleaned_data['keyword']#追加 foods = LikeFood.objects.filter(content__contains=keyword)#追加 else: foods = LikeFood.objects.all() context = { 'foods':foods, 'searchForm':serachForm#追加 } return render(request,'testapp/index.html',context) 2・views.pyにフォームオブジェクト追加 from .forms import SerachForm・・・forms.pyに記述したクラスを読み込み serachForm = SerachForm(request.GET)・・・Getメソッドのrequestで得た情報を用いインスタンス化 ざっとしたPOSTとGETの違い HTTP GET:指定したURLの内容をWebサーバから取り出す(GET) HTTP POST:クライアントが入力した内容をWebサーバに送る(POST) 引用:以下URL https://medium-company.com/http-get-post-%E9%81%95%E3%81%84/ serachForm.is_valid()・・・Form インスタンスで、 is_valid()メソッドを呼び出す時は検証を実行し、データが有効かどうかを示すブール値を返す。 serachForm.cleaned_data['keyword']・・・ バリデーションを通過してきた入力値が格納されている辞書データからkeywordの入力値を取り出している。 バリデーションとは・・・ 入力されたデータが、あるいはプログラミング言語やマークアップ言語の記述が、規定された文法に即して、または要求された仕様にそって、適切に記述されているかどうかを検証することである。 LikeFood.objects.filter(content_contains=keyword)・・・filterメソッドをしようして限定している。 <フィールド名>_<検索条件>==< 値>で絞り込み可能 検索条件はgt(greater than)などあるので調べる必要あり。 APP/testapp/templates/testapp/index.html {% extends './base.html' %} {% block content %} {% if searchForm %}<!--追加--> <form action='{% url "testapp:index" %}' method='get'><!--追加--> <div class='form-group'><!--追加--> {{searchForm}}<!--追加--> <input type="submit" class="btn btn-outline-primary" value="OK" /><!--追加--> <a href="{% url 'testapp:index' %}" class="btn btn-outline-secondary">クリア</a><!--追加--> </div><!--追加--> </form><!--追加--> {% endif %}<!--追加--> <table class='table table-striped table-hover'> {% for food in foods %} <tr> <td>{{ food.content }}</td> <td>{{ food.color }}</td> <td><a href='{% url "testapp:detail" food.id %}' class='btn btn-outline-primary'>詳細</a></td> <td><a href='{% url "testapp:delete" food.id %}' class='btn btn-outline-secondary'>削除</a></td> </tr> {% endfor %} </table> <a href='{%url "testapp:create" %}'>新規</a> {% endblock %} 3・テンプレートにフォーム追加 これで検索した文字を含んでいるデータを表示することはできた!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FastAPIの認証付き開発環境を整える

TL;DR コードはここに一式置いてあります。 https://github.com/inunekousapon/easy_fastapi 環境 MacOS Big Sur バージョン11.4 Docker Desktop Version 3.4.0 作るもの FastAPIが動くこと MySQLと繋がっていること 認証の仕組みが整っていること JWTで認証と認可ができること FastAPIのDockerを作る Github公式 https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8 COPY ./app/requirements.txt /app RUN pip install -r requirements.txt ライブラリをインストールするためにappディレクトリを作成し、requirements.txtを追加します。 . ├── Dockerfile ├── app │ └── requirements.txt requirements.txtにはJWTの認証に必要なライブラリとデータベースの接続に必要なものを指定しておきます。 requirements.txt python-jose[cryptography]== 3.3.0 passlib[bcrypt]==1.7.4 SQLAlchemy==1.4.22 pymysql==1.0.2 python-multipart==0.0.5 MySQLを環境に加える docker-compose.ymlを加えていきます。 MySQLの設定は下記のサイトを参考にしています。 https://qiita.com/ucan-lab/items/b094dbfc12ac1cbee8cb docker-compose.yml version: "3" services: db: image: mysql:8.0 volumes: - db-store:/var/lib/mysql - ./logs:/var/log/mysql - ./docker/mysql/my.cnf:/etc/mysql/conf.d/my.cnf environment: - MYSQL_DATABASE=${DB_NAME} - MYSQL_USER=${DB_USER} - MYSQL_PASSWORD=${DB_PASS} - MYSQL_ROOT_PASSWORD=${DB_PASS} - TZ=${TZ} ports: - ${DB_PORT}:3306 web: build: . ports: - 80:80 command: /start-reload.sh volumes: - ./app:/app environment: - DB_NAME=${DB_NAME} - DB_USER=${DB_USER} - DB_PASS=${DB_PASS} - DB_PORT=${DB_PORT} - DB_HOSTNAME=db volumes: db-store: docker/mysql/my.cnf # MySQLサーバーへの設定 [mysqld] # 文字コード/照合順序の設定 character-set-server = utf8mb4 collation-server = utf8mb4_bin # タイムゾーンの設定 default-time-zone = SYSTEM log_timestamps = SYSTEM # デフォルト認証プラグインの設定 default-authentication-plugin = mysql_native_password # エラーログの設定 log-error = /var/log/mysql/mysql-error.log # スロークエリログの設定 slow_query_log = 1 slow_query_log_file = /var/log/mysql/mysql-slow.log long_query_time = 5.0 log_queries_not_using_indexes = 0 # 実行ログの設定 general_log = 1 general_log_file = /var/log/mysql/mysql-query.log # mysqlオプションの設定 [mysql] # 文字コードの設定 default-character-set = utf8mb4 # mysqlクライアントツールの設定 [client] # 文字コードの設定 default-character-set = utf8mb4 DB_NAME=homestead DB_USER=homestead DB_PASS=secret DB_PORT=3306 TZ=Asia/Tokyo ディレクトリ構成はこうなったはずです。 . ├── Dockerfile ├── app │ └── requirements.txt ├── docker │ └── mysql │ └── my.cnf └── docker-compose.yml アプリケーションを書く appフォルダ以下にmain.pyを加えていきます。 app/main.py from fastapi import FastAPI app = FastAPI() @app.get("/") async def root(): return {"message": "Hello World"} ディレクトリ構成です。 . ├── Dockerfile ├── app │ ├── app.py │ └── requirements.txt ├── docker │ └── mysql │ └── my.cnf └── docker-compose.yml 起動 起動していきましょう。 docker-compose up しばらくすると下記のような表示が出るはずです。 web_1 | INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit) web_1 | INFO: Started reloader process [1] using watchgod web_1 | INFO: Started server process [8] web_1 | INFO: Waiting for application startup. web_1 | INFO: Application startup complete. http://0.0.0.0:80 にアクセスすると下記のレスポンスが得られるはずです。 {"message":"Hello World"} http://0.0.0.0:80/docs にアクセスするとSwagger的なドキュメントを見ることができます。 データベース連携 appフォルダ以下に database.py というファイルを追加します。 MySQLと接続するために必要です。 app/database.py import os from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker user = os.getenv("DB_USER") password = os.getenv("DB_PASS") dbname = os.getenv("DB_NAME") hostname = os.getenv("DB_HOSTNAME") SQLALCHEMY_DATABASE_URL = f"mysql+pymysql://{user}:{password}@{hostname}/{dbname}" engine = create_engine(SQLALCHEMY_DATABASE_URL) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() appフォルダ以下に models.py というファイルを追加します。 テーブルの定義をします。 app/models.py from sqlalchemy import Boolean, Column, Integer, String from sqlalchemy.orm import relationship from database import Base class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) username = Column(String(120), index=True) full_name = Column(String(120)) email = Column(String(200), unique=True, index=True) hashed_password = Column(String(60)) disabled = Column(Boolean, default=True) app/main.pyに下記を足します。 本来ならばcreate_allは利用せずにmigrationツールを使いますので注意してください。 models.Base.metadata.create_all(bind=engine) app/main.py from fastapi import FastAPI import models from database import engine models.Base.metadata.create_all(bind=engine) app = FastAPI() @app.get("/") async def root(): return {"message": "Hello World"} このままだとMySQLが起動する前にFastAPIサーバーが起動してしまうので起動を待つようにします。 prestart.shというファイルをapp配下に置きます。 app/prestart.sh #! /usr/bin/env bash # Let the DB start sleep 10; 再度起動するとデータベースにテーブルが作成されます。 OAuth2認証とJWT 下記の内容になります。 https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/ app/main.pyに書き足していきます。 app/main.py from datetime import datetime, timedelta from typing import Optional from fastapi import Depends, FastAPI, HTTPException, status from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from jose import JWTError, jwt from passlib.context import CryptContext from pydantic import BaseModel from sqlalchemy.orm import Session import models from database import engine, SessionLocal SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 class Token(BaseModel): access_token: str token_type: str class TokenData(BaseModel): username: Optional[str] = None class User(BaseModel): username: str email: Optional[str] = None full_name: Optional[str] = None disabled: Optional[bool] = None class UserInDB(User): hashed_password: str models.Base.metadata.create_all(bind=engine) pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") app = FastAPI() def get_db(): db = SessionLocal() try: yield db finally: db.close() def verify_password(plain_password, hashed_password): return pwd_context.verify(plain_password, hashed_password) def get_password_hash(password): return pwd_context.hash(password) def get_user(db, username: str): return db.query(models.User).filter(models.User.username == username).first() def authenticate_user(db, username: str, password: str): user = get_user(db, username) if not user: return False if not verify_password(password, user.hashed_password): return False return user def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=15) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") if username is None: raise credentials_exception token_data = TokenData(username=username) except JWTError: raise credentials_exception user = get_user(db, username=token_data.username) if user is None: raise credentials_exception return user @app.post("/token", response_model=Token) async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)): user = authenticate_user(db, form_data.username, form_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={"sub": user.username}, expires_delta=access_token_expires ) return {"access_token": access_token, "token_type": "bearer"} @app.get("/") async def root(current_user: User = Depends(get_current_user)): return {"message": "Hello World"} データベースのusersに下記のレコードを追加します。 id username full_name email hashed_password disabled 1 johndoe John Doe johndoe@example.com $2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW 使い方 http://localhost:docs に繋ぎましょう。 / Root を開いて「Try it out」押し、Executeボタンを押すと401 Unauthorizedが返ってくるのが確認できます。 右上のAuthorizeボタンを押すと認証フォームが表示されるので、usernameに「johndoe」、passwordに「secret」と入力して「Authorize」ボタンを押します。 再度、/Root を開いて先程と同じ操作をすると、今度はHTTP200が返ってきます。 ハマりどころ SQLAlchemyとMySQLが意外とセットアップ難しい。 FastAPIのドキュメントで認証のバックエンドがフェイクなのでDBとの連携する箇所が分かりづらい。 pydanticのスキーマとSQLAlchemyのスキーマとリクエストのスキーマの3種類のスキーマが存在するのが分かりづらい。 参考サイト uvicorn-gunicorn-fastapi-docker https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker MySQL8.0のコンテナ作成 https://qiita.com/ucan-lab/items/b094dbfc12ac1cbee8cb
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonでSAS/Stataファイルを読み込む

SASファイルとStataファイル SAS (Statistical Analysis System)ファイルはビジネスアナリティクスや生物統計学で多く使われています。 Stata (Statistics + Data)ファイルは経済学や疫学などの学術的な社会科学研究で使われています。 SASファイルの読込み 一般的なSASファイルの拡張子は.sas7bdatになります。読込みはsas7bdatパッケージのSAS7BDAT関数で行います。 from sas7bdat import SAS7BDAT with SAS7BDAT('test.sas7bdat') as file: df = file.to_data_frame() Stataファイルの読込み Stataファイルの拡張子は.dtaになります。pandasを使って読み込めます。 import pandas as pd df = pd.read_stata('test.dta')
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

写真から人間の3Dモデルを出力できたら、イラストに活用できるよねという話。- TCMR- その1

はじめに 動画を読み込んで骨格検知して検出した動きを3Dモデルに当てはめてみようと思います。 (といってるけど、この記事ではアルゴリズムの内容の把握とデモを動かすだけにとどまる。) なお、当記事は前回の記事にも記載したSMPLの準備が必須になっています。 (前回の記事は↓↓↓) https://qiita.com/akaiteto/items/b5c8c3d5eb5ca3849c5d 前回記事でダウンロードしたソースは使いませんが、 同梱しているpklというバイナリ化された3Dモデルのファイルが必要なのでそちらを準備して下さい。 TCMR はじめに https://github.com/hongsukchoi/TCMR_RELEASE 今回試してみるのはCVPR2021に出展していた研究です。 https://arxiv.org/pdf/2011.08627.pdf 論文も読みますがひとまずデモソースを動かしてみましょう。 前提条件 私の環境は以下の通り googleアカウント     何でもいい。 google colab     pythonをブラウザから実行できるとんでもサービス。     pipのコマンドはもちろん、apt-getなどのLinux系のコマンドも実行できるしGPUも使える。     あまりにも研究向けの無料サービス SMPLのモデルファイル     前回の記事参照 とりあえずデモ動かす githubに手順が書かれているのでそのとおりに行えば良いだけです。 が、google colabで行うので自分のために備忘録として残します。 3Dモデルアップロード google colabのサーバーに3Dモデルをアップロードします。 この3Dモデルに最終的な骨格が当てはめられるので必須の作業です。 まず、赤枠を選択してpklファイルを選択します。 上述していますが、pklファイル(3Dモデル)はSMPLからダウンロードしてください。(前提条件参照) pklのファイルを選択します。3つありますが男か女か程度の差異なので適当に選びます。 このファイルには3Dモデルの情報がバイナリ化されて入っています。 アップロード中。左下のプログレスバーが完了するまで待ちます。 時間がかかります。待たずにやると当然ですがファイルが不完全なので、 プログラムの実行部分で読み込みエラーが出ます。待ちましょう。 実行環境準備 ソースを引っ張ってきて必要なライブラリを実行します。 google colabを使う前提なので、コマンドのまえにビックリマークがついています。 !git clone https://github.com/hongsukchoi/TCMR_RELEASE !pip install numpy==1.17.5 torch==1.4.0 torchvision==0.5.0 !pip install git+https://github.com/giacaglia/pytube.git --upgrade !pip install -r TCMR_RELEASE/requirements.txt なお、公式を見るとシェルを叩いて環境を構築しますが、 google colabの環境だとどうにも上手く行かなかったので内容を手打ちしています。 手打ちで省いた箇所は、仮想環境構築の部分です。 google colabはサイクルで環境がリセットされるのでこれで問題有りませんが、 自分のPCでやる方は仮想環境を・・・というかシェルから実行しましょう。 3Dモデル配置 「3Dモデルアップロード」の手順で実行しているはずですが、 pklファイルをアップロードしてください。 デモ環境準備 デモソースを実行するための準備をします。 ! mkdir output ; cd output ; mkdir demo_output ! mkdir data ; cd data ; mkdir base_data ! mv basicmodel_f_lbs_10_207_0_v1.1.0.pkl data/base_data/basicmodel_f_lbs_10_207_0_v1.1.0.pkl ! cp data/base_data/basicmodel_f_lbs_10_207_0_v1.1.0.pkl data/base_data/SMPL_NEUTRAL.pkl !source TCMR_RELEASE/scripts/get_base_data.sh 最終的にこのようになってればOK。 やってることは出力先のフォルダ作ってリネームしただけです。 また、get_base_data.shで必要な入力動画データと学習済みのモデルをダウンロードしてます 最終的にこうなってればOK デモ環境準備 !python TCMR_RELEASE/demo.py --vid_file demo.mp4 --gpu 0 これを実行して成功すればでも完了です。 実行します。 エラー1:ファイル破損 error.py Traceback (most recent call last): File "TCMR_RELEASE/demo.py", line 376, in <module> main(args) File "TCMR_RELEASE/demo.py", line 103, in main hidden_size=1024 File "/content/TCMR_RELEASE/lib/models/tcmr.py", line 131, in __init__ self.regressor = Regressor() File "/content/TCMR_RELEASE/lib/models/spin.py", line 229, in __init__ create_transl=False, File "/content/TCMR_RELEASE/lib/models/smpl.py", line 65, in __init__ super(SMPL, self).__init__(*args, **kwargs) File "/usr/local/lib/python3.7/dist-packages/smplx/body_models.py", line 188, in __init__ encoding='latin1')) _pickle.UnpicklingError: could not find MARK はいエラー。smplxのモデルの読み込みのところでエラーが出ています。 https://github.com/vchoutas/smplx/blob/77cf2a7010370c1e44141fab5d15ad8a0841bc9d/smplx/body_models.py#L2348 このあたりでエラーが出ている模様。 ファイルの存在チェック諸々はされているので、ファイルの存在云々ではなさそう。 test.py ## pip install smplx==0.1.13 ## pip install chumpy from smplx import SMPL as SMPL SMPL_MODEL_DIR = 'data/base_data' smpl = SMPL( SMPL_MODEL_DIR, batch_size=64, create_transl=False ).to('cpu') 今のままだと調査しづらいのでエラー箇所を抽出します。 アップロードしてる最中にファイルが壊れたかな?ローカル環境で実行したところ、 問題なく動作しました。ファイルを再アップロードします。 余談ですが、SMPLの3DモデルにはSMPL,SMPLX,SMPLHの3つがあるようで、 はじめインストールしているライブラリ名からして、SMPLXのモデルじゃないことが原因かと思いましたが、 通常のSMPLのモデルでも問題なさそうです。 更に余談ですが、 smplで一番初めにインスタンを立ち上げる際、3Dモデルを指定する部分で フォルダ名で指定すると、モデル名はデフォルトで「SMPL_NEUTRAL.pkl」で読み込もうとするので注意です。 エラー2:GPUエラー Traceback (most recent call last): File "TCMR_RELEASE/demo.py", line 376, in <module> main(args) File "TCMR_RELEASE/demo.py", line 103, in main hidden_size=1024 File "/content/TCMR_RELEASE/lib/models/tcmr.py", line 134, in __init__ pretrained_dict = torch.load(pretrained)['model'] File "/usr/local/lib/python3.7/dist-packages/torch/serialization.py", line 529, in load return _legacy_load(opened_file, map_location, pickle_module, **pickle_load_args) File "/usr/local/lib/python3.7/dist-packages/torch/serialization.py", line 702, in _legacy_load result = unpickler.load() File "/usr/local/lib/python3.7/dist-packages/torch/serialization.py", line 665, in persistent_load deserialized_objects[root_key] = restore_location(obj, location) File "/usr/local/lib/python3.7/dist-packages/torch/serialization.py", line 156, in default_restore_location result = fn(storage, location) File "/usr/local/lib/python3.7/dist-packages/torch/serialization.py", line 132, in _cuda_deserialize device = validate_cuda_device(location) File "/usr/local/lib/python3.7/dist-packages/torch/serialization.py", line 116, in validate_cuda_device raise RuntimeError('Attempting to deserialize object on a CUDA ' RuntimeError: Attempting to deserialize object on a CUDA device but torch.cuda.is_available() is False. If you are running on a CPU-only machine, please use torch.load with map_location=torch.device('cpu') to map your storages to the CPU. エラーが出ましたがメッセージ変わりましたね。さっきのエラーは潰せました。 このエラー典型的な「お前GPUもってないやないか!」というメッセージです。 googlecolabでもGPUは使えますが、使用回数限られているのでCPUで動かすようコードを変えます。 # TCMR_RELEASE/lib/models/tcmr.py # 134行目 # pretrained_dict = torch.load(pretrained)['model'] # 以下のように変更 pretrained_dict = torch.load(pretrained,map_location=torch.device('cpu'))['model'] # TCMR_RELEASE/demo.py # 108行目 # ckpt = torch.load(pretrained_file) ckpt = torch.load(pretrained_file,map_location=torch.device('cpu')) エラー3:次元エラー Traceback (most recent call last): File "TCMR_RELEASE/demo.py", line 378, in <module> main(args) File "TCMR_RELEASE/demo.py", line 113, in main model.load_state_dict(ckpt, strict=False) File "/usr/local/lib/python3.7/dist-packages/torch/nn/modules/module.py", line 830, in load_state_dict self.__class__.__name__, "\n\t".join(error_msgs))) RuntimeError: Error(s) in loading state_dict for TCMR: size mismatch for regressor.smpl.shapedirs: copying a param with shape torch.Size([6890, 3, 10]) from checkpoint, the shape in current model is torch.Size([6890, 3, 300]) エラーが出ました。 学習済みのモデルを読み込もうとしたら次元が違うらしい。 このエラーは実装の内容理解してからじゃないととけないかもしれないー。 この節の続きに追記がなかったら、多分失敗している・・・ 追伸.時間がないので後で書く。20210727 前提知識 論文を読みます。・・・の前に、前提知識を整理します。 Residual Network(ResNet) さて、2015年ごろの研究ではVGGの研究成果により、 CNNの層を従来よりもかなり深くしたことでより高度で細かい特徴の抽出に成功しました。 ResNetでも同じように階層深くしようぜ!!・・・と思ってやってみたが、どうにも上手く行きません そこで考え方を変えましょうよ、と。 一般的なニュートラルネットワークは出力される値に付随するパラメータを学習することで最適な解を得るけれど、 ResNetでは出力される値に入力の値も加えて学習するらしい。(なぜそういう解決法の発想に至ったかは知らない) 上図で言うところの入力を加えている矢印が「residual connection」。 後述する「residual connection」です。なお、上図のひとかたまりを「residual block」と呼びます。 Resnetを導入したことにより、階層を増やしてもしっかり学習ができ、 かつ高度な細かい特徴も抽出できるようになったので表現力があがりました。 34層の階層(resne340)の実装例を見てみます。 https://github.com/CellEight/PytorchResNet/blob/main/models/ResNet34.py 下記の最初の部分だけ一部適当に抜粋しながら見ます。図の通り、3つのresidual connectionがあるので、 3つの「residual block」がある構成です。 test.py class ResBlock(nn.Module): def __init__(self,*args,res_transform=None): super().__init__() # 畳み込み層、活性化関数などの各処理を一連して行う用セット self.seq = nn.Sequential(*args) self.res_transform = res_transform def forward(self,x): if self.res_transform: x0 = self.res_transform(x) else: x0 = x # 出力される値(通常のネットワークの出力) x = self.seq(x) # 入力の値も加える。(residual connectionにあたる実装) return x + x0 class ResNet34(nn.Module): def __init__(self, n_classes): # 3つのResidualBlockを定義 self.ResidualBlock1 = ResBlock(nn.Conv2d(64,64,kernel_size=3,stride=1,padding=1,padding_mode='reflect'), \ nn.BatchNorm2d(64), \ nn.ReLU(), \ nn.Conv2d(64,64,kernel_size=3,stride=1,padding=1,padding_mode='reflect'), \ nn.BatchNorm2d(64), \ nn.ReLU()) self.ResidualBlock2 = ResBlock(nn.Conv2d(64,64,kernel_size=3,stride=1,padding=1,padding_mode='reflect'), \ nn.BatchNorm2d(64), \ nn.ReLU(), \ nn.Conv2d(64,64,kernel_size=3,stride=1,padding=1,padding_mode='reflect'), \ nn.BatchNorm2d(64), \ nn.ReLU()) self.ResidualBlock3 = ResBlock(nn.Conv2d(64,64,kernel_size=3,stride=1,padding=1,padding_mode='reflect'), \ nn.BatchNorm2d(64), \ nn.ReLU(), \ nn.Conv2d(64,64,kernel_size=3,stride=1,padding=1,padding_mode='reflect'), \ nn.BatchNorm2d(64), \ nn.ReLU()) def forward(self,x): # Block x = self.ResidualBlock1(x) x = self.ResidualBlock2(x) x = self.ResidualBlock3(x) Global Average Pooling https://qiita.com/mine820/items/1e49bca6d215ce88594a 多すぎるパラメータを減らそうぜ!しかもこれやると、精度も良くなるよ! という話らしい。 例えば、512枚の大量の画像特徴があると、その画像のピクセルの数分だけ重みの調整が必要になるけど、 画像それぞれを平均とって、その平均値に対してのみ重みつけようぜ!という話みたい。 RNN/LSTM/GRU/bidirectional GRUs RNN/LSTM https://qiita.com/akaiteto/items/d5f0d615916877091571 昔記事に書いてた通り。 RNNは連続で時系列のあるデータを扱うのが得意。 前回の出力結果も今回の計算に含めて計算することで、前回の結果も含めた結果を出すことができる。 ただその場合、 「前回って言うけど、どこまでが前回? 「全フレーム参照する気か?時間かかるし、余計なものも使ってしまいそうだけど。」 という問題に対する対応がLSTM。 各フレームごとに重要度を設定して、重要じゃないフレームは無視するし、 重要なら未来永劫反映させるように設定する。 GRU https://www.slideshare.net/gakhov/recurrent-neural-networks-part-1-theory LSTMの変形らしい。利点はLSTMほどメモリ食わないしLSTMよりも高速だとか。 LSTMの$C_t$というのが、各フレームの重要度をもっている関数なんだけど、 GRUではこれがなくなっている。各フレームの重要度を担っていた$C_t$が入出力を司る$h_t$に集約されたのがGRU bidirectional GRU/RNN/LSTM pytorch的に言えば、「bidirectional=True」を設定するだけで良いらしい。 https://medium.com/@felixs_76053/bidirectional-gru-for-text-classification-by-relevance-to-sdg-3-indicators-2e5fd99cc341 http://colah.github.io/posts/2015-09-NN-Types-FP/ 今までは過去の出力を加えてたけど、未来の出力も活用することで、 現在の状態がどのようになってるか予想しようぜ!という構造のもの。 へぇ。わからんけど、実際の難しい処理はpytorch先生におまかせしよう。 Weak perspective(弱透視) 要するに正射影。 http://www.thothchildren.com/chapter/5c16710c41f88f26724b1748 ピンホールカメラのパラメータとしては、おなじみ内部パラメータ、外部パラメータなどがある。 後の文章に出てくる弱透視カメラのパラメータは・・・なんだろう? 出力として弱透視カメラのパラメータが得られるらしいけど、ピンホールカメラと同じようなもの? と思いながら論文を読み進むると、スケールと移動の値が出力されるらしいので、 外部パラメータと同じようなものが出力されるらしい。 論文読む イントロ これまで多くの研究により、単一の画像から3Dモデルを推定することには成功しました。 その研究の多くは、SMPLの各関節の動きの情報をパラメータとして与えて回帰(予測)することで 動きの推定を行います。 さて、本手法のテーマは「動画」からの人間骨格の推定です。 単一画像を想定した従来方法で動画の動きを推定しようとすると、フレーム単体で推定を行うため、 全体の挙動として動きががなめらかじゃなかったり、一連の動きの一貫性が失われて正確な推定ができません。 これに対して動画に拡張するための手法として下記のような手法が提案されています。 1.全ての入力フレームから骨格に関わる(staticな)特徴を取得。 2.全てのフレームの特徴を時間的な特徴にエンコードするエンコーダーに渡す。 3.エンコードされた時間的な特徴から各入力フレームの骨格を推定する。 (Learning 3D Human Dynamics from Video https://github.com/akanazawa/human_dynamics) (VIBE :https://github.com/mkocabas/VIBE) しかしこれらの手法にも問題があります。検出したポースと時間が一致しない問題です。 ・・・まぁ要するに、それでもやっぱりスムーズにポーズが動かないと。 https://youtu.be/WB3nTnSQDII?t=53 ↑従来方法(VIBE)と本手法を比較すると顕著ですが、従来方法は動きがどこかスムーズじゃありません。 原因は上記手順の1にある(staticな)特徴と、3の時間的な特徴が一致していないことにあるらしいです。 なぜこのような不一致がおきるのか?曰く、原因は2つあるらしい。 1つ目は、入力フレームごとの(staticな)特徴に極端に強く依存してしまう点。 より具体的には、「上述1番の(staticな)特徴」と「上述2番の時間的な特徴」、 これらをを結びつける「residual connection」(前提知識参照)にある。 ResNetをそのまま使うと、時間的な特徴の学習を妨げてしまうらしく、 時間的な特徴の推定の精度がよくないらしい。 時間的な特徴の情報は骨格を推定する上では活用できる情報なので、 時間的な特徴の情報の推定が上手く活用しないと、骨格の推定は上手く行かない。 本稿ではしっかりそのあたりも対策してるのが強み。 2つ目は、上述の2番の時間的な特徴をエンコードするエンコーダーの問題。 現在のフレーム、過去のフレーム、未来のフレームを考えます。 従来方法だと現在のフレームは、上述二番のエンコーダーの出力に影響を与える可能性は最も高く、 過去や未来のフレームを含めての学習ができていないことが問題です。 本稿ではしっかりそのあたりも対策してるのが強み。 もう少し読む 1.全ての入力フレームから骨格に関わる(staticな)特徴を取得。 2.全てのフレームの特徴を時間的な特徴にエンコードするエンコーダーに渡す。 3.エンコードされた時間的な特徴から各入力フレームの骨格を推定する。 (Learning 3D Human Dynamics from Video https://github.com/akanazawa/human_dynamics) (VIBE :https://github.com/mkocabas/VIBE) 動画を読み込んでフレームごとの画像$I_1 \cdots I_T$を取得します。 (staticな)特徴を取得 これについては従来手法におまかせしましょう。 (https://github.com/nkolot/SPIN) 従来研究でトレーニングされているものを使って、単一の画像から(staticな)特徴を取得します。 これに対して、「global average pooling(前提知識参照)」を行うことで更に変換し、$f_1 \cdots f_T \in R^{2048}$を取得します。 (ぶっちゃけ私がやりたいことはこのOSSだけでいい) 時間的な特徴を取得 まず考え方として、フレームを現在、過去、未来と分けて考えます。 現在のフレームは、T個のフレームのうちの$T/2$番目のものを現在のフレームとして定義します。 VIBE "https://github.com/mkocabas/VIBE" では、 双方向ゲート付き回帰ユニット(bi-directional gated recurrent unit ) というものを使って、時間特徴にエンコードします。(前提知識参照) 双方向ゲート付き回帰ユニットとは、要するに 未来方向・過去方向計算された2つの方向のGRUを利用して、対象となる現在の時間的な特徴を計算する方法です。 本稿でも同様に、bidirectionalGRUで計算します。ただし全て同じでは有りません。 本稿の構図としては、$T/2$番目の「現在」と呼ばれる$f_{T/2}$の時間的な特徴を計算すべく、 $f_1 \cdots f_T \in R^{2048}$の過去から未来のすべての入力フレ―ムを使って推定します。 すなわち、$f_{T/2}$からみて過去の$1 \cdots \frac{T}{2}-1$をGRUで計算した集合$g_{past}$、 $f_{T/2}$からみて未来の$\frac{T}{2}+1 \cdots T$をGRUで計算した集合$g_{future}$より、 未来と過去の双方向から計算するbidirectionalGRUで、現在の状態を推定します。 VIBEと違う点としては、VIBEでは下図の矢印のようにResNetの「residual connection」を採用しているかいないかの点です。 「residual connection」は、上述の通り滑らかさが欠如する問題の原因となっているので、本稿ではその点を改善しています。 (下図はVIBEの論文。矢印のところが「residual connection」) さて、過去、現在、未来のGRUの結果が格納された$g_{past}$、$g_{future}$、$g_{all}$を、最終的に1つに統合します。 活性化関数(Relu)を使って$g^{'}{past}$、$g^{'}{future}$、$g^{'}_{all}$を取得し、3つを1つに統合。 そこからさらに、「attention values」を現在過去未来ごとに出力します。(詳細省略) 「attention values」は、現在・過去・未来のそれぞれの出力結果にどれくらいの重みをつけるか、 どれくらい反映させるかを決定します。これが最終的な出力$g_{int}$。 トレーニングの段階では、 $g_{past}$、$g_{future}$、$g_{all}$をregressorに渡して、$\Theta$という出力を得ます。 $\Theta$は各パラメータの和集合で構成されており、SMPLを動かすための角度の情報と、 弱透視カメラのパラメータ(スケール、平行移動)[weak-perspective camera parameter]、 アイデンティティパラメータ(なにそれ)の出力結果が、過去未来現在ごとに格納されます。 損失関数 現在過去未来の最終的な出力と、正解の値とをL2損失で計算します。 より詳細には、3次元の関節の座標、2次元の関節の座標、そしてSMPLパラメータから計算します。 2次元の関節の座標については、3次元の関節の座標より、取得した弱透視カメラモデルのパラメータで計算します。 実際の実装 以下箇条書き。 ・VIBEに従いフレーム間隔$T$は16にセット。 ・ビデオのFPSは26-30 ・単一画像からの姿勢推定はhttps://github.com/nkolot/SPINを使用。モデルも多分同じ。 ・最適化関数はAdamでミニバッチサイズは32。 ・(staticな)特徴を取得では、Resnetでトリミングされた画像から計算することで時間とメモリを節約している。 ・すべての回転情報は、Zhou etalの6D回転表現で予測し、最終的にはSMPL向けにaxis-angle 表現の回転表現に変換します。 ソースを見る ソース見ながら内容を見てみます。完全に私のメモ代わりの書き置きです。 あと、デモソース動かしたときにエラーがでたので原因をダメ元でしらべたい…。 人物のトラッキング https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/demo.py#L82 下記OSSで動画から複数人のトラッキングを行います。YOLOV3 & MaskRCNNによる人間の検出 https://github.com/mkocabas/multi-person-tracker TCMRモデル準備1 https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/demo.py#L98 TCMR(この記事の技術)のモデルを呼び出します。 TCMRの内部には、「時間的な特徴」と「Regressor」の2つのネットワークがあります。 TemporalEncoderネットワーク:時間的な特徴 https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/lib/models/tcmr.py#L88 $f_1 \cdots f_T \in R^{2048}$のT枚の画像の特徴情報をわたして、 現在過去未来ごとにGRUの計算結果$g_{all},g_{past},g_{future}$を取得します。 そしてここからは下記画像の部分。 https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/lib/models/tcmr.py#L93 まず$g_{all},g_{past},g_{future}$Reluで活性化させつつ、次元が2048になるように変換。 ー> $g^{'}{past}$、$g^{'}{future}$、$g^{'}_{all}$ https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/lib/models/tcmr.py#L95 3つの情報を2048×256に統合し、 https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/lib/models/tcmr.py#L11 「attention values」の3つを出力。そのまま、図と同じように$g^{'}{past}$、$g^{'}{future}$、$g^{'}_{all}$とtorch.mulで乗算。 Regressorネットワーク:SMPLのパラメータ「等」を予測する 時間がないので後で書く。20210727 TCMRモデル準備2 https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/demo.py#L106 そして事前に学習した重みを読み込ませます。 ここには「attention values」が入ってる・・・はず? あれ?ちがうかも? SPINモデル準備 https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/demo.py#L124 ここまでの説明で言うところの、「(staticな)特徴」を出力するネットワークを準備する。 下記の既存の研究を呼び出している。 https://github.com/nkolot/SPIN 人ごとに全フレームの骨格を取得 https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/demo.py#L164 全フレーム読み込んで人単位で骨格を取得。 最終的な出力の初期化。 https://github.com/hongsukchoi/TCMR_RELEASE/blob/8078b3c39c22cae39eb19c0e1eb70e09c60ecea7/demo.py#L180 ここで初期化されている値がpredict(予測)される出力値。regressorから帰ってくる$\Theta$が入ってる。 SMPLのパラメータ${\theta , \beta}$、そして、弱透視カメラのパラメータ${s,t}$(スケール、移動量)、 それからSMPLの3次元上の関節座標と、それを二次元平面に投射した2次元上の関節座標が入ってきます。 最終的な出力の初期化。 TCMRモデルに骨格の特徴を与えて、本稿のアルゴリズムに従い 過去未来現在のGRUを計算することで時間的な特徴を取得し、$g_{int}$を取得します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでのstatic変数/static関数について考える

1. はじめに  C++でコードを書いていく中で、「static変数」という変数宣言が出てきた。Pythonではデコレータとしてstaticmethodは標準搭載されているものの、「static変数」については搭載されていないようである。  今回はC++でstatic変数を用いて実装したコードをPythonで書き換えてみて、Pythonではいかにして「static変数」を実装すべきかを考えるとともに、なぜPythonでは「static変数」の概念がなく、staticmethodだけが残されたかの理由について考えてみる。 2. static変数とは こちらのstatic変数についての解説を下記に引用した。 プログラム中で使用する変数のうち、プログラムの開始から終了まで値が保持され続けるもの。特に、通常は生成と破棄を繰り返す関数やメソッド内部のローカル変数について、同じ内容を維持し続けるよう指定したもの。 上記を見てインスタンス変数では定義するのは難しいと感じた。というのもクラスをnewするたびに、そのインスタンス変数として定義していては、それぞれインスタンス間での変数の保持に不向きである。 3. C++のスクリプト  今回はstatic変数について考える上で、Carクラスがインスタンス化されるたびに、車の台数をカウントするスクリプトを例として作成した。このスクリプト内では、Carクラス内にnewしたインスタンス数を数える変数( = car_count )をstatic変数、アクセス指定子をprivateに指定し、car_countを表示するためのstatic関数としてshowCounter関数を実装し、アクセス指定子はpublicとした。  出力結果からも分かるようにnewするたびに車の台数が1台増え、deleteするたびに1台減っており、car_countがstatic変数として機能していることが確認できる。 main.cpp #include "car.cpp" #include <iostream> using namespace std; int main() { Car *car1, *car2, *car3; car1 = new Car(); Car::showCounter(); car2 = new Car(); Car::showCounter(); delete car1; Car::showCounter(); car3 = new Car(); Car::showCounter(); delete car2; Car::showCounter(); delete car3; Car::showCounter(); return 0; } car.h (ヘッダーファイル)  #ifndef _CAR_H_ #define _CAR_H_ class Car { public: // Constructor Car(); // Destructor ~Car(); static void showCounter(); // static関数 private: static int car_count; // static変数 }; #endif car.cpp #include "car.h" #include <iostream> using namespace std; int Car::car_count = 0; // Constructor Car::Car() { car_count++; } // Destructor Car::~Car() { car_count--; } // 車の台数の出力 void Car::showCounter() { cout << "車の台数は現在 " << car_count << " 台です" << endl; } 出力 車の台数は現在 1 台です 車の台数は現在 2 台です 車の台数は現在 1 台です 車の台数は現在 2 台です 車の台数は現在 1 台です 車の台数は現在 0 台です 4. Pythonのスクリプト 4-1. 方針  さて、上記C++で実装したスクリプトをPythonでも同出力となるように作成した。インスタンス数をカウントするstatic変数を実装していくわけだが、2. static変数とはにも書いたとおり、以下の方針でスクリプトを作成した。 class外で仮想的にstatic変数 ( car_count ) を実装 class内でclass変数を仮想的にstatic変数として実装 4-2. 実装 4-2-1. class外で仮想的にstatic変数を実装  class外で、countNum関数内でcountNum.counterをstatic変数に見立てて実装した。出力結果も同一であるが、countNum関数はクラス外で定義しているためアクセス指定子などは指定できず、どこからでもcountNum.counterの値にアクセスして変更できてしまう点が問題である。また、Carクラス内にcountNum関数が実装されていないことで、何のカウンターなのかを瞬時に判断することが難しく、可読性が下がるとともに、スクリプトもやや冗長になりがち。 example1.py def transStaticVar(var_name, value): def decorate(function): setattr(function, var_name, value) return function return decorate @transStaticVar("counter", 0) def countNum(counter_flag): """ @param counter_flag True-> + , False -> - """ if counter_flag: countNum.counter += 1 else: countNum.counter -= 1 def showCounter(): print("車の台数は現在 {} 台です".format(countNum.counter)) class Car(object): def __init__(self, counter_flag=True): """ Constructor """ countNum(counter_flag) def __del__(self, counter_flag=False): """ Destructor """ countNum(counter_flag) if __name__ == "__main__": car1 = Car() showCounter() car2 = Car() showCounter() del car1 showCounter() car3 = Car() showCounter() del car2 showCounter() del car3 showCounter() 出力 車の台数は現在 1 台です 車の台数は現在 2 台です 車の台数は現在 1 台です 車の台数は現在 2 台です 車の台数は現在 1 台です 車の台数は現在 0 台です ■ class内でclass変数を仮想的にstatic変数として実装  class変数として__counterをstatic変数に見立てて実装した。出力結果も同一である。4-2-1. class外で仮想的にstatic変数を実装で問題点として上げた外部からの書き換え問題も、class変数をprivate化することにより、外部から値の変更ができないように制御できた。また、C++同様に現在のcounter数を表示させるshowCounter関数をstatic関数としてクラス内に組み込むことにより、容易に何のカウンターを表しているかが分かるとともに可読性があがる。 example2.py class Car(object): __counter = 0 @staticmethod def showCounter(): print("車の台数は現在 {} 台です".format(Car.__counter)) def __init__(self): """ Constructor """ Car.__counter += 1 def __del__(self): """ Destructor """ Car.__counter -= 1 if __name__ == "__main__": car1 = Car() Car.showCounter() car2 = Car() Car.showCounter() del car1 Car.showCounter() car3 = Car() Car.showCounter() del car2 Car.showCounter() del car3 Car.showCounter() 出力 車の台数は現在 1 台です 車の台数は現在 2 台です 車の台数は現在 1 台です 車の台数は現在 2 台です 車の台数は現在 1 台です 車の台数は現在 0 台です まとめ  結局のところ、クラス変数 ≒ static変数なのでPythonではわざわざ「static変数」として宣言しなくなったということかもしれない。ただし、scopeを問わずにアクセスできると、変数値の書き換えなどを予期せぬ位置で容易にできてしまいバグの原因になり得るので、private変数化して、変数アクセスにgetterをstatic関数として準備しておくのがベストプラクティスなのかもしれない。  上記実装で、static変数の実装ができているという仮定のもとであれば、Pythonに「static変数」の概念はないが、static関数は標準搭載されていることの意義は納得できる。 ※ご意見等ありましたらコメントいただけたら幸いです! 参考サイト 静的変数(スタティック変数)とは Pythonの関数内の静的変数とは何ですか?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MLflowのProductionのモデルのバージョンの確認方法

記載内容 MLflowで管理しているモデルについて、実行時のログに、バージョン番号も記録したい。 Productionのステージにあるモデルのバージョン番号の確認方法を調査する。 MLflowのProductionのモデルのバージョンの確認方法 REST APIで実行したら、レスポンスヘッダーにモデルバージョン番号が入ってないかな、 モデルをロードした場合は、そのモデルがバージョン番号持ってないかな、 と期待したのですが、ありませんでした。 MLflowに対して、モデル名でModelVersionを検索して、ステージがProductionのモデルのバージョン番号を確認する必要があるようです。 REST APIで確認する方法 Search ModelVersionsが使えます。 mlflow document > Search ModelVersions リクエストデータのfilterに、"name='my-model-name'"といったように、モデル名を絞り込み条件に入れてリクエストすると、[ModelVersion]の配列が返ってきます。current_stageがProductionのModelVersionのバージョン番号を確認します。 残念ながら、ステージでの絞り込みは現状対応していないようです。 String filter condition, like “name=’my-model-name’”. Must be a single boolean condition, with string values wrapped in single quotes. Python APIで確認する方法 mlflow.tracking.MlflowClient.search_model_versionsが使えます。 mlflow document > mlflow.tracking.MlflowClient.search_model_versions REST APIと同じ要領です。メソッドの引数に、"name='my-model-name'"と入れて実行します。 filter_string – A filter string expression. Currently, it supports a single filter condition either a name of model like name = 'model_name' or run_id = '...'.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

(python)idとはなんだ??

◆はじめに 突発的な興味からpythonを勉強し始めた大学生です。 これまでC言語・C++・C#を学び使っています、ので比較的すんなり入れたのですが、それでも疑問に思う部分や壁にぶつかる事は少なくありません。備忘録を兼ねてまとめていきますので、同じ疑問をお持ちになった方是非お役立てください。 今回は、id関数についてです。 初見でなんだこれは、いつ使うんだ、数字出てくるけれどこれに何の有用性が...となったので調べてみました。 ◆id関数とは Pythonは「オブジェクト指向言語」と呼ばれるプログラミング言語であり、あらゆる情報を「オブジェクト」という単位で管理しています。オブジェクトには「オブジェクトID」という識別子が与えられて管理されています。 どうやらオブジェクトとオブジェクトに付与されたIDは1:1対応しており、idによってオブジェクトを特定できるようです。 イメージが湧きにくいですが、数字・文字列・リスト・タプルなどまで全てオブジェクトです。 ◆試してみる ひとまず手を動かして確認してみます。 print(id(1)) #数字 print(id("python")) #文字列 print(id([1,2,3])) #リスト print(id((10,20,30))) #タプル 実行結果↓ 140735205943088 3005763814640 3005795551296 3005795206592 確かにそれぞれ異なるIDが付与されています。 それでは、id関数の引数に変数を入れた場合はどうなるのでしょうか。 x=1 y="python" print(id(x)) print(id(y)) 実行結果↓ 140735205943088 3005763814640 #数字や文字列を直接指定した時と変わらない 引数に変数を指定した場合は、変数が参照しているオブジェクトのIDが取得できます。 ※ここまで読んだ方ならお分かりになるかと思いますが、オブジェクトIDは識別に用いられる番号にすぎません。つまりある意味相対的なものであるため、これらの出力はプログラムを実行する環境に依存することに注意してください。  ◆終わりに 初めて見た時は戸惑いましたが、なんだか便利そうですね(こなみ) ここまでお読みいただきありがとうございました。 参考記事[https://monozukuri-c.com/python-funclist-id/]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

深層学習。wandbでAPI keyがpasteできない場合の対処方法

深層学習。wandbでAPI keyがpasteできない場合の対処方法 タイトルの内容を示す。 wandb: You can find your API key in your browser here: https://wandb.ai/authorize xxxxx Paste an API key from your profile and hit enter: 上記のところで何故かAPI Keyが入力できないことあり。 Windowsにて。 対策 以下の形式で、最初にAPI keyを入れる。 wandb login 13d61343820eaa8xxxxxxxx0dd203416da9df37c まとめ 特にありません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでやってみた設計パターンの実装

エンティティの実装 構成 自己紹介 エンティティとは エンティティの実装 ドメインモデル データモデル 適用パターン まとめ 自己紹介 カキギカツユキ ネット通販の会社で業務システムを開発運用しています あと、売掛金・買掛金管理の管理業務しています その前はシステムエンジニアとしていろんな会社のシステム開発をしていました エンティティとは 多くのオブジェクトは、本質的に、その属性によってではなく、連続性と同一性(identity)によって定義される。 エリック・エヴァンスのドメイン駆動設計 ドメインの概念をエンティティとして設計するのは、その同一性を気にかけるときだ。つまり、システム内の他のオブジェクトと区部が必須の制約となっている時である。エンティティは一意なものであり、長期にわたって変わり続けることができる。変わりかたはさまざまなので、オブジェクトが、かつてあった状態からまったく変わってしまうこともあるだろう。しかし、見た目が変わっても、それらは同一のオブジェクトである。 実践ドメイン駆動設計 エンティティの実装 from abc import ABCMeta, abstractmethod import unittest from unittest.case import doModuleCleanups from sqlalchemy.engine import create_engine from sqlalchemy.orm.session import sessionmaker from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, Integer, String, ForeignKey from enum import Enum Base = declarative_base() class TestUser(unittest.TestCase): def setUp(self) -> None: self.user = User() self.user.name = "カキギ カツユキ" self.user.address = "733-00xx 広島県広島市西区xx町1-2-3 123" self.user.role = Role.管理者 def test_名前を登録できる(self): self.assertEqual(self.user.name, "柿木 勝之") def test_住所を登録できる(self): self.assertEqual(self.user.address, "733-00xx 広島県広島市西区xx町1-2-3 123") def test_権限を登録できる(self): self.assertEqual(self.user.role, Role.管理者) class TestRepository(unittest.TestCase): def test_ユーザを登録できる(self): user = User() user.name = "カキギ カツユキ" user.address = "733-00xx 広島県広島市西区xx町1-2-3 123" user.role = Role.利用者.value repo = SQLiteRepository() repo.add_user(user) self.assertEqual(repo.get_user(1).name, "カキギ カツユキ") self.assertEqual(repo.get_user(1).address, "733-00xx 広島県広島市西区xx町1-2-3 123") self.assertEqual(repo.get_user(1).role, Role.利用者.value) class Role(Enum): 管理者 = 1 利用者 = 2 class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) name = Column(String) address = Column(String) role = Column(Integer) class Repository: def __init__(self): self.users = [] def add_user(self, user): self.users.append(user) def get_user(self, index): return self.users[index] class Repository(metaclass=ABCMeta): @abstractmethod def add_user(self, user): pass @abstractmethod def get_user(self, index): pass class SQLiteRepository(Repository): def __init__(self): self.engine = create_engine("sqlite:///:memory:") self.session = sessionmaker(bind=self.engine)() self.conn = self.engine.connect() Base.metadata.create_all(self.engine) def add_user(self, user): self.session.add(user) self.session.commit() def get_user(self, index): return self.session.query(User).get(index) 適用パターン リポジトリ 組み込みバリュー 値オブジェクト リポジトリ ドメインオブジェクトにアクセスするためのコレクション型インターフェースを使って、ドメインとデータマッピングレイヤとの仲介をする。 エンタープライズアーキテクチャパターン リポジトリ class Repository(metaclass=ABCMeta): @abstractmethod def add_user(self, user): pass @abstractmethod def get_user(self, index): pass class SQLiteRepository(Repository): def __init__(self): self.engine = create_engine("sqlite:///:memory:") self.session = sessionmaker(bind=self.engine)() self.conn = self.engine.connect() Base.metadata.create_all(self.engine) def add_user(self, user): self.session.add(user) self.session.commit() def get_user(self, index): return self.session.query(User).get(index) 組み込みバリュー オブジェクトを他のオブジェクトテーブルの複数フィールドにマッピングする。 エンタープライズアーキテクチャパターン 組み込みバリュー class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) first_name = Column(String) last_name = Column(String) prefecture = Column(String) city = Column(String) twon = Column(String) room = Column(String) role_type = Column(Integer) def __init__(self, name: Name, address: Address, role=Role.利用者): self.last_name = name.last self.first_name = name.first self.postal_code = address.postal_code.value self.prefecture = address.prefecture self.city = address.city self.twon = address.twon self.room = address.room self.role_type = role.value @property def name(self): return Name(first=self.first_name, last=self.last_name) @property def address(self): return Address(postal_code=PostalCode(self.postal_code), prefecture=self.prefecture, city=self.city, twon=self.twon, room=self.room) @property def role(self): return Role(self.role_type) 値オブジェクト IDに基づいた等価性を確保していない、MoneyやDate Rangeなどのシンプルな小型オブジェクト。 エンタープライズアーキテクチャパターン 値オブジェクト class Name: def __init__(self, first: str, last: str) -> None: self.first = first self.last = last def __str__(self) -> str: return f"{self.first} {self.last}" def __eq__(self, o: object) -> bool: return self.first == o.first and self.last == o.last def __hash__(self) -> int: return hash(self.first) ^ hash(self.last) class PostalCode: def __init__(self, value: str) -> None: self.value = value def __str__(self) -> str: return f"{self.value}" def __eq__(self, o: object) -> bool: return self.value == o.value def __hash__(self) -> int: return hash(self.value) class Address: def __init__(self, postal_code: PostalCode, prefecture: str, city: str, twon: str, room: str) -> None: self.postal_code = postal_code self.prefecture = prefecture self.city = city self.twon = twon self.room = room def __str__(self) -> str: return f"{self.postal_code} {self.prefecture}{self.city}{self.twon} {self.room}" def __eq__(self, o: object) -> bool: return self.postal_code == o.postal_code and self.prefecture == o.prefecture and self.city == o.city and self.twon == o.twon and self.room == o.room def __hash__(self) -> int: return hash(self.postal_code) ^ hash(self.prefecture) ^ hash(self.city) ^ hash(self.twon) ^ hash(self.room) ドメインモデル 振る舞いとデータの両方を一体化させたドメインのオブジェクトモデル。 エンタープライズアーキテクチャパターン ドメインモデル データモデル まとめ 正直めんどいけどすぐに元はとれる、値オブジェクトまじ便利。 エンティティ、名前知らなくても多分使ってる。 以前はアクティブレコードパターンをよく聞いていたけど、最近はリポジトリパターンをよく聞く。 データモデルとのインピーダンスミスマッチはいろいろな解決アプローチがある。 参照 エンタープライズアプリケーションアーキテクチャパターン マーチン・ファウラー (著), 株式会社テクノロジックアート (翻訳), 長瀬嘉秀 (翻訳, 監修) 現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法 増田 亨 (著) エリック・エヴァンスのドメイン駆動設計 Eric Evans (著), 和智右桂 (翻訳), 牧野祐子 (翻訳), 今関剛 (監修) 実践ドメイン駆動設計 (Object Oriented SELECTION) ヴォーン・ヴァーノン (著), 髙木 正弘 (翻訳)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonでやってみた設計パターンの実装

値オブジェクトの実装 構成 自己紹介 値オブジェクトとは マネーの実装 まとめ 自己紹介 カキギカツユキ ネット通販の会社で業務システムを開発運用しています あと、売掛金・買掛金管理の管理業務しています その前はシステムエンジニアとしていろんな会社のシステム開発をしていました 値オブジェクトとは IDに基づいた等価性を確保していない、MoneyやDate Rangeなどのシンプルな小型オブジェクト。 エンタープライズアーキテクチャパターン 値の種類ごとに専用の型を用意するコードが安定し、コードの意図が明確になります。このように、値を扱うための専用クラスを作るやり方を値オブジェクト(Value Object)と呼びます。 現場で役立つシステム設計の原則 エンティティの同一性を追跡するのは本質的なことだが、それ以外のオブジェクトに同一性を与えてしまうと、システムの性能を損なうことになり、分析作業が増え、さらに、すべてのオブジェクトの見た目が同じになってしまうことでモデルが台無しになりかねない。 エリック・エヴァンスのドメイン駆動設計 マネーの実装 import unittest import hashlib class Money: def __init__(self, amount, currency) -> None: self.__amount = amount self.__currency = currency def __str__(self) -> str: if self.__currency == 'JPY': return f'¥{self.__amount}' elif self.__currency == 'USD': return f'${self.__amount}' else: return f'{self.__amount}' def __eq__(self, other: object) -> bool: return(self.__amount == other.__amount and self.__currency == other.__currency) def __hash__(self) -> int: encoded_currency = int(hashlib.sha256( self.__currency.encode("utf-8")).hexdigest(), 16) % (10 ** 8) return hash(self.__amount + encoded_currency) def add(self, other: object) -> object: if self.__currency != other.__currency: raise ValueError('異なる通貨では計算できません') return Money(self.__amount + other.__amount, self.__currency) class TestMoney(unittest.TestCase): def setUp(self) -> None: self.千円 = Money(1000, 'JPY') self.一万円 = Money(10000, 'JPY') self.千ドル = Money(1000, 'USD') def test_金額を表示する(self): self.assertEqual(str(self.千円), '¥1000') self.assertEqual(str(self.一万円), '¥10000') def test_外貨金額を表示する(self): self.assertEqual(str(self.千ドル), '$1000') def test_金額は等しい(self): self.assertEqual(self.千円, Money(1000, 'JPY')) self.assertNotEqual(self.千円, self.千ドル) def test_通貨を保持している(self): 財布 = {self.千円} self.assertTrue(self.千円 in 財布) self.assertFalse(self.千ドル in 財布) def test_金額を合計する(self): 二千円 = self.千円.add(Money(1000, 'JPY')) self.assertEqual(str(二千円), '¥2000') with self.assertRaises(ValueError, msg='異なる通貨では計算できません'): self.千ドル.add(Money(1000, 'JPY')) unittest.main(argv=[''], verbosity=2, exit=False) まとめ 正直めんどくさい 慣れてくると普通に便利 基本形を指で覚える 参照 エンタープライズアプリケーションアーキテクチャパターン マーチン・ファウラー (著), 株式会社テクノロジックアート (翻訳), 長瀬嘉秀 (翻訳, 監修) 現場で役立つシステム設計の原則 〜変更を楽で安全にするオブジェクト指向の実践技法 増田 亨 (著) エリック・エヴァンスのドメイン駆動設計 Eric Evans (著), 和智右桂 (翻訳), 牧野祐子 (翻訳), 今関剛 (監修)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonでAtCoderをやるときの個人的チートシート

はじめに タイトルの通り、PythonでAtCoderをやるときのメモです。個人的というか自分用というかそんな感じなので、ミスがあったりもっと良い書き方があったりするかもしれません。随時更新していく予定です。現在書き途中です。大体出来上がった後は随時更新を予定しています。 ちょっとした小技というかメモ A,BのA/Bの切り上げ C = -(-A // B) 配列の重複を消す array = list(set(array)) 累積和 最後にBに[0]を入れておくといいことがあるかも。 import itertools #Aは前のリスト BにAの累積和が入る B = list(itertools.accumulate(A)) 最大公約数、最小公倍数 math.gcdは遅いみたいな噂をちらっと聞いたのでこれで最大公約数、最小公倍数を出します。 #最大公約数 def gcd(a, b): if a < b: a, b = b, a while b: a, b = b, a % b return a #最小公倍数 def lcm(a, b): return a * b //(gcd(a, b)) 素因数分解 $O(\sqrt{N})$でできます。 def prime_factorize(n): a = [] while n % 2 == 0: a.append(2) n //= 2 f = 3 while f * f <= n: if n % f == 0: a.append(f) n //= f else: f += 2 if n != 1: a.append(n) return a エラトステネスの篩を前処理として行い($O(N\log\log N)$)、その後素因数分解する($O(\log N)$) #前処理 エラトステネスの篩 #素因数分解する数の最大値 MAX = 10**6 D = [i for i in range(MAX+1)] for i in range(2,MAX+1): if D[i] != i:continue for j in range(2*i,MAX+1,i): D[j] = i def prime_factorize(n): tmp = [] while n != 1: tmp.append(D[n]) n //= D[n] return tmp 約数列挙 $O(\sqrt{N})$でできます。どこかからのコピペです。どこからか思い出したら書きます。 def make_divisors(n): lower_divisors , upper_divisors = [], [] i = 1 while i*i <= n: if n % i == 0: lower_divisors.append(i) if i != n // i: upper_divisors.append(n//i) i += 1 return lower_divisors + upper_divisors[::-1] 組み合わせ ${}_nP_r,{}_nC_r $です。mod pの下の組み合わせは気が向いたら書きます。 import math def permutations_count(n, r): return math.factorial(n) // math.factorial(n - r) def combinations_count(n, r): return math.factorial(n) // (math.factorial(n - r) * math.factorial(r)) リストで列挙するには以下のように書けばよいです。n!の列挙はperminationsでrをnにすればよいです。 import itertools #n = 4 , r = 2 のとき、 A = list(itertools.permutations(range(n),r)) #A = [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)] A = list(itertools.combinations(range(n),r)) #A = [(0, 1), (0, 2), (0, 3), (1, 0), (1, 2), (1, 3), (2, 0), (2, 1), (2, 3), (3, 0), (3, 1), (3, 2)] めぐる式二分探索 def is_ok(x): #条件を満たすかどうか def meguru_bisect(ng,ok): #条件を満たす最小のokを返す #ng,okは 取りうる値の最小-1, 取りうる値の最大+1 #条件を満たす最大を求めたいときはng,okを逆にすればよい while abs(ok-ng) > 1: mid = (ok + ng) // 2 if is_ok(mid): ok = mid else: ng = mid return ok 参考にしたサイト:https://aotamasaki.hatenablog.com/entry/meguru_bisect 三分探索 下に凸の関数の最小値を求めます。 def f(x): #最小値を求めたい関数 #最小値がある範囲 high = 10**18 low = 0 while high - low > 0.000000001: #許容される誤差 mid_left = high/3+low*2/3 mid_right = high*2/3+low/3 if f(mid_left) >= f(mid_right): low = mid_left else: high = mid_right #f(high)がf(x)の最小値 参考にしたサイト:https://juppy.hatenablog.com/entry/2019/04/11/ARC054_-B H行W列のマス目を左上から斜めにとる どう表現すればよいのかわかりません... for i in range(H + W - 1): for j in range(i + 1): h = i - j w = i - h if h >= H or w >= W:continue #S[h][w]に対する処理 例えばH=4,W=3のとき、h,wは [[0, 0]] [[1, 0], [0, 1]] [[2, 0], [1, 1], [0, 2]] [[3, 0], [2, 1], [1, 2]] [[3, 1], [2, 2]] [[3, 2]] こんな感じになります。書くときによくlist index out of rangeを出してしまうのであらかじめメモとして書くことにします。ARC120Bで使いました。もっといい書き方を見つけたら書き直します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

t-SNEの改良版!q-SNE

2021/7/27 投稿 0. この記事の対象者 python, sklearnをある程度扱える人 次元削減手法q-SNEについて知りたい人 q-SNEを使ってみたい人 1. はじめに この記事ではICPR2020で発表された論文「q-SNE: Visualizing Data using q-Gaussian Distributed Stochastic Neighbor Embedding」の解説と実装を行う. 解説は論文の内容を元にしたもので行い,実装は著者のgithubにあるものを使って行う. 元論文 Python Github 2. 論文解説 2-1. 簡潔に言うと タイトルより「q-SNE: Visualizing Data using q-Gaussian Distributed Stochastic Neighbor Embedding」とあるようにt-SNEがt分布を使っているのに対してq-SNEはqガウス分布というものを使っている. これによりt−SNEよりも直感的に次元削減後の形を操作できるようになる. 2-2. qガウス分布 qガウス分布はガウス分布の拡張版で,以下のように定義される. P_q(x;\mu,\sigma^2)=\frac{1}{Z_q}\left( 1+\frac{q-1}{3-1}\frac{(x-\mu)^2}{\sigma^2}\right)^{-\frac{1}{q-1}}\\ Z_q = \begin{cases} \sqrt{\frac{3-q}{q-1}}Beta\left(\frac{3-q}{2(q-1)},\frac{1}{2}\right)\sigma & 1\leq q<3\\ \sqrt{\frac{3-q}{1-q}}Beta\left(\frac{2-q}{1-q},\frac{1}{2}\right)\sigma & q<1 \end{cases} 上式のようにqガウス分布はハイパーパラメータqを持っており,このqを操作することで次元削減後の埋め込みの形を操作する. qガウス分布は以下のような確率分布である. 分布から見てわかるように,qが1に近づいていくとガウス分布と一致していき,qが2に近づくとt分布に一致する. 実際にq=1(極限で1)になるとガウス分布,q=2になるとt分布になるようになっている. この図は分布の係数部分$Z_q$を含んでいるが,t-SNEないしq-SNEは計算時に係数部分を使用しないため係数を省いた分布もみてみる. この図を見るとqが大きくなるほど中心が細くなり,裾が大きくなっている. この分布の変化がわかれば,直感的にqを操作してサンプルの近さを近づけたり離したりできるようになる. 2-3. q-SNEの式 i). 高次元の類似度 SNE系統はこの高次元の類似度を教師無しでいかに評価することがポイントとなっている. まず,式は以下のようになっている. p_{j|i}=\frac{\exp{(-\|\boldsymbol{x}_j-\boldsymbol{x}_i\|^2/2\sigma_i^2)}}{\sum_{k\neq i}^N\exp{(-\|\boldsymbol{x}_k-\boldsymbol{x}_i\|^2/2\sigma_i^2)}} ここで$N$はサンプル数,$\boldsymbol{x}$は$D$次元のサンプルとする. 分子に注目するとサンプル$\boldsymbol{x}_i$というベクトルを平均(中心)としたあるサンプル$\boldsymbol{x}_j$についての多次元ガウス分布となっている. 同様に分母は$\boldsymbol{x}_i$を中心としたガウス分布の全サンプルの総和であることがわかる. これは$i$番目のサンプルと$j$番目のサンプルは全体的にどれくらい近いよ,というのを測っている. (ちなみにガウス分布の係数部分は分母分子で打ち消してなくなっている) ここで多次元ガウス分布の共分散行列をここではスカラーの$\sigma_i$で近似しており,これは以下の式で求める \log{k}=\sum_{j\neq i}^Np_{j|i}\log{p_{j|i}} この$k$をperplexityというハイパーパラメータで決め,上式が成り立つような$\sigma$をバイナリーサーチで求める. これでサンプル$\boldsymbol{x}$周りの類似度を決定し,これを全サンプル分用意する. しかし,このままでは対象性がなく,$p_{j|i}\neq p_{i|j}$となってしまうので,以下のようにして対称性を作る. p_{ij} = \frac{1}{2}(p_ip_{j|i}+p_jp_{i|j})=\frac{p_{j|i}+p_{i|j}}{2N} 上の式では$p_i=p_j=\frac{1}{N}$として求まる. この$p_{ij}$を高次元サンプルの類似度として扱う. ii). 低次元の類似度 低次元の類似度はqガウス分布を使うと以下のようになる. r_{ij}=\frac{\left(1+\frac{q-1}{3-q}\|\boldsymbol{y}_j-\boldsymbol{y}_i\|^2\right)^{-\frac{1}{q-1}}}{\sum_l^N\sum_{k\neq l}^N\left(1+\frac{q-1}{3-q}\|\boldsymbol{y}_k-\boldsymbol{y}_l\|^2\right)^{-\frac{1}{q-1}}} ここで$\boldsymbol{y}$は$d$次元($d<<D$)の次元削減後のベクトルである. 上式のように次元削減後のベクトル間の類似度を高次元のときと同じように測っている. ここで違う点としては低次元側では分散を1として扱い,分母の総和の際は全サンプル間の類似度の総和を使っている. (qガウス分布の係数部分は分母分子で打ち消してなくなっている) ちなみにq=2にすると r_{ij}=\frac{\left(1+\frac{2-1}{3-2}\|\boldsymbol{y}_j-\boldsymbol{y}_i\|^2\right)^{-\frac{1}{2-1}}}{\sum_l^N\sum_{k\neq l}^N\left(1+\frac{2-1}{3-2}\|\boldsymbol{y}_k-\boldsymbol{y}_l\|^2\right)^{-\frac{1}{2-1}}}=\frac{\left(1+\|\boldsymbol{y}_j-\boldsymbol{y}_i\|^2\right)^{-1}}{\sum_l^N\sum_{k\neq l}^N\left(1+\|\boldsymbol{y}_k-\boldsymbol{y}_l\|^2\right)^{-1}} となり,完全にt-SNEの式と一致する. iii). 最適化 高次元の分布に低次元の分布を近づけることで次元削減を行う. 最適化する式は以下の通り. C=\sum_{i}^N\sum_{j\neq i}^Np_{ij}\log{\frac{p_{ij}}{r_{ij}}} これはカルバックライブラーダイバージェンスという分布間の近さを評価する損失関数である. これで$p_{ij}$と$r_{ij}$が近づき,$\boldsymbol{y}$がきれいに可視化されていく. その$\boldsymbol{y}$の更新式は以下の通り. \boldsymbol{y}_i^{t+1}=\boldsymbol{y}_i^t-\eta\frac{\partial C}{\partial \boldsymbol{y}_i}+\alpha(t)(\boldsymbol{y}_i^t-\boldsymbol{y}_i^{t-1})\\ \frac{\partial C}{\partial \boldsymbol{y}_i}=\frac{4}{3-q}\sum_j^N(p_{ij}-r_{ij})(\boldsymbol{y}_j-\boldsymbol{y}_i)\left(1+\frac{q-1}{3-q}\|\boldsymbol{y}_j-\boldsymbol{y}_i\|^2\right)^{-1} ここで$t$はイテレーション,$\eta$は学習率, $\alpha(t)$はモーメンタムを意味する. 3. python実装 実装は上記にも上げたgithubのコードを用いて行う(ここの解説が必要と言われれば追記します). リポジトリをローカルにcloneし,python環境を整える. cythonを使用して高速実装をしているため,OSによってcython周りの環境は注意して整える. (cythonがうまく行かない場合はQSNE.pyの126行目を"_utils._binary_search_perplexity"から"_binary_search_perplexity"に変えれば実行時間は遅くなるが実行できる) q=2の時の実装プログラムは以下 import numpy as np from QSNE import QSNE import matplotlib.pyplot as plt from sklearn import datasets digits = datasets.load_digits() qsne = QSNE(n_components=2, q=2.0, verbose=1) X_reduced = qsne.fit_transform(digits.data) target = digits.target for i in range(10): plt.scatter(X_reduced[target==i, 0], X_reduced[target==i, 1], label=str(i)) plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, ncol=1) plt.show() 出力結果は以下 qをq=1.1に変えて実装した結果は以下 qを小さくするとqガウス分布の中心周りが太くなるため次元削減したマップも大きく広がっている. qをq=2.5に変えて実装した結果は以下 qを大きくするとqガウス分布の中心周りが細くなるため次元削減したマップもきゅっと小さくなる.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【論文解説:python実装】t-SNEの改良版!q-SNE

2021/7/27 投稿 0. この記事の対象者 python, sklearnをある程度扱える人 次元削減手法q-SNEについて知りたい人 q-SNEを使ってみたい人 1. はじめに この記事ではICPR2020で発表された論文「q-SNE: Visualizing Data using q-Gaussian Distributed Stochastic Neighbor Embedding」の解説と実装を行う. 解説は論文の内容を元にしたもので行い,実装は著者のgithubにあるものを使って行う. 元論文 Python Github 2. 論文解説 2-1. 簡潔に言うと タイトルより「q-SNE: Visualizing Data using q-Gaussian Distributed Stochastic Neighbor Embedding」とあるようにt-SNEがt分布を使っているのに対してq-SNEはqガウス分布というものを使っている. これによりt−SNEよりも直感的に次元削減後の形を操作できるようになる. 2-2. qガウス分布 qガウス分布はガウス分布の拡張版で,以下のように定義される. P_q(x;\mu,\sigma^2)=\frac{1}{Z_q}\left( 1+\frac{q-1}{3-1}\frac{(x-\mu)^2}{\sigma^2}\right)^{-\frac{1}{q-1}}\\ Z_q = \begin{cases} \sqrt{\frac{3-q}{q-1}}Beta\left(\frac{3-q}{2(q-1)},\frac{1}{2}\right)\sigma & 1\leq q<3\\ \sqrt{\frac{3-q}{1-q}}Beta\left(\frac{2-q}{1-q},\frac{1}{2}\right)\sigma & q<1 \end{cases} 上式のようにqガウス分布はハイパーパラメータqを持っており,このqを操作することで次元削減後の埋め込みの形を操作する. qガウス分布は以下のような確率分布である. 分布から見てわかるように,qが1に近づいていくとガウス分布と一致していき,qが2に近づくとt分布に一致する. 実際にq=1(極限で1)になるとガウス分布,q=2になるとt分布になるようになっている. この図は分布の係数部分$Z_q$を含んでいるが,t-SNEないしq-SNEは計算時に係数部分を使用しないため係数を省いた分布もみてみる. この図を見るとqが大きくなるほど中心が細くなり,裾が大きくなっている. この分布の変化がわかれば,直感的にqを操作してサンプルの近さを近づけたり離したりできるようになる. 2-3. q-SNEの式 i). 高次元の類似度 SNE系統はこの高次元の類似度を教師無しでいかに評価することがポイントとなっている. まず,式は以下のようになっている. p_{j|i}=\frac{\exp{(-\|\boldsymbol{x}_j-\boldsymbol{x}_i\|^2/2\sigma_i^2)}}{\sum_{k\neq i}^N\exp{(-\|\boldsymbol{x}_k-\boldsymbol{x}_i\|^2/2\sigma_i^2)}} ここで$N$はサンプル数,$\boldsymbol{x}$は$D$次元のサンプルとする. 分子に注目するとサンプル$\boldsymbol{x}_i$というベクトルを平均(中心)としたあるサンプル$\boldsymbol{x}_j$についての多次元ガウス分布となっている. 同様に分母は$\boldsymbol{x}_i$を中心としたガウス分布の全サンプルの総和であることがわかる. これは$i$番目のサンプルと$j$番目のサンプルは全体的にどれくらい近いよ,というのを測っている. (ちなみにガウス分布の係数部分は分母分子で打ち消してなくなっている) ここで多次元ガウス分布の共分散行列をここではスカラーの$\sigma_i$で近似しており,これは以下の式で求める \log{k}=\sum_{j\neq i}^Np_{j|i}\log{p_{j|i}} この$k$をperplexityというハイパーパラメータで決め,上式が成り立つような$\sigma$をバイナリーサーチで求める. これでサンプル$\boldsymbol{x}$周りの類似度を決定し,これを全サンプル分用意する. しかし,このままでは対象性がなく,$p_{j|i}\neq p_{i|j}$となってしまうので,以下のようにして対称性を作る. p_{ij} = \frac{1}{2}(p_ip_{j|i}+p_jp_{i|j})=\frac{p_{j|i}+p_{i|j}}{2N} 上の式では$p_i=p_j=\frac{1}{N}$として求まる. この$p_{ij}$を高次元サンプルの類似度として扱う. ii). 低次元の類似度 低次元の類似度はqガウス分布を使うと以下のようになる. r_{ij}=\frac{\left(1+\frac{q-1}{3-q}\|\boldsymbol{y}_j-\boldsymbol{y}_i\|^2\right)^{-\frac{1}{q-1}}}{\sum_l^N\sum_{k\neq l}^N\left(1+\frac{q-1}{3-q}\|\boldsymbol{y}_k-\boldsymbol{y}_l\|^2\right)^{-\frac{1}{q-1}}} ここで$\boldsymbol{y}$は$d$次元($d<<D$)の次元削減後のベクトルである. 上式のように次元削減後のベクトル間の類似度を高次元のときと同じように測っている. ここで違う点としては低次元側では分散を1として扱い,分母の総和の際は全サンプル間の類似度の総和を使っている. (qガウス分布の係数部分は分母分子で打ち消してなくなっている) ちなみにq=2にすると r_{ij}=\frac{\left(1+\frac{2-1}{3-2}\|\boldsymbol{y}_j-\boldsymbol{y}_i\|^2\right)^{-\frac{1}{2-1}}}{\sum_l^N\sum_{k\neq l}^N\left(1+\frac{2-1}{3-2}\|\boldsymbol{y}_k-\boldsymbol{y}_l\|^2\right)^{-\frac{1}{2-1}}}=\frac{\left(1+\|\boldsymbol{y}_j-\boldsymbol{y}_i\|^2\right)^{-1}}{\sum_l^N\sum_{k\neq l}^N\left(1+\|\boldsymbol{y}_k-\boldsymbol{y}_l\|^2\right)^{-1}} となり,完全にt-SNEの式と一致する. iii). 最適化 高次元の分布に低次元の分布を近づけることで次元削減を行う. 最適化する式は以下の通り. C=\sum_{i}^N\sum_{j\neq i}^Np_{ij}\log{\frac{p_{ij}}{r_{ij}}} これはカルバックライブラーダイバージェンスという分布間の近さを評価する損失関数である. これで$p_{ij}$と$r_{ij}$が近づき,$\boldsymbol{y}$がきれいに可視化されていく. その$\boldsymbol{y}$の更新式は以下の通り. \boldsymbol{y}_i^{t+1}=\boldsymbol{y}_i^t-\eta\frac{\partial C}{\partial \boldsymbol{y}_i}+\alpha(t)(\boldsymbol{y}_i^t-\boldsymbol{y}_i^{t-1})\\ \frac{\partial C}{\partial \boldsymbol{y}_i}=\frac{4}{3-q}\sum_j^N(p_{ij}-r_{ij})(\boldsymbol{y}_j-\boldsymbol{y}_i)\left(1+\frac{q-1}{3-q}\|\boldsymbol{y}_j-\boldsymbol{y}_i\|^2\right)^{-1} ここで$t$はイテレーション,$\eta$は学習率, $\alpha(t)$はモーメンタムを意味する. 3. python実装 実装は上記にも上げたgithubのコードを用いて行う(ここの解説が必要と言われれば追記します). リポジトリをローカルにcloneし,python環境を整える. cythonを使用して高速実装をしているため,OSによってcython周りの環境は注意して整える. (cythonがうまく行かない場合はQSNE.pyの126行目を"_utils._binary_search_perplexity"から"_binary_search_perplexity"に変えれば実行時間は遅くなるが実行できる) q=2の時の実装プログラムは以下 import numpy as np from QSNE import QSNE import matplotlib.pyplot as plt from sklearn import datasets digits = datasets.load_digits() qsne = QSNE(n_components=2, q=2.0, verbose=1) X_reduced = qsne.fit_transform(digits.data) target = digits.target for i in range(10): plt.scatter(X_reduced[target==i, 0], X_reduced[target==i, 1], label=str(i)) plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, ncol=1) plt.show() 出力結果は以下 qをq=1.1に変えて実装した結果は以下 qを小さくするとqガウス分布の中心周りが太くなるため次元削減したマップも大きく広がっている. qをq=2.5に変えて実装した結果は以下 qを大きくするとqガウス分布の中心周りが細くなるため次元削減したマップもきゅっと小さくなる.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TOYPRO解説 計算順序(400)

問題概要 整数と四則演算、括弧からなる式が与えられるので、計算順序を表す括弧を付ける問題 解説 いわゆる構文解析というやつです 普通に字句解析して構文解析してやってもいいんですが、Python にはast(https://docs.python.org/ja/3/library/ast.html) があるので活用しましょう TOYPRO の Python は使える標準ライブラリが制限されているため、astを import して使うことは出来ないのですが、組み込みのcompile関数のflags(第四引数)に1024(型注釈をパースする場合は5120)を渡すことで 文字列を ast オブジェクトにパースすることができます あとはこれを DFS しながら式を再構築してやれば AC できます 想定解コード e = "1+2*3" def visit_ast(node): node_name = node.__class__.__name__ if node_name == "BinOp": left = visit_ast(node.left) right = visit_ast(node.right) op = { "Add": "+", "Sub": "-", "Mult": "*", "Div": "/" }.get(node.op.__class__.__name__) return "".join(("(", left, op, right, ")")) elif node_name == "Num": return str(node.n) elif node_name == "Constant": return str(node.value) ast = compile(e, "<string>", "eval", 1024).body print(visit_ast(ast))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Djangoの旅 ~Part10~ リダイレクト編

目標 共通テンプレート習得 流れ 1・index.html(共通化したいhtmlファイル)をbase.html(共通部分をまとめるファイル)にコピー 2・base.htmlで共通部分以外をタグで囲む 3・index.htmlファイルの共通部分をタグで囲む 4・確認 コード解説 $ cp testapp/templates/testapp/index.html testapp/templates/testapp/base.html 1・index.html(共通化したいhtmlファイル)をbase.html(共通部分をまとめるファイル)にコピー cpコマンドを用いてindex.htmlをbase.htmlに複製 APP/testapp/templates/testapp/base.html <!DOCTYPE html> <html> <head> <meta charset = 'utf-8'> <title>好きな食べ物</title> </head> <body> {% block content%}<!--追加 --> {% endblock %}<!--追加--> </body> </html> 2・base.htmlで共通部分以外をタグで囲む {% block content%} {% endblock %} ・・・この2つで囲まれた部分(共通部分以外)はhtmlごとに表示内容が変更され、それ以外の部分は共通部分として変動せずどのhtmlでも表示できるようになる。 これによって修正などがスムーズに行えるようになる!!! APP/testapp/templates/testapp/index.html {% extends './base.html' %}<!--追加 --> {% block content %}<!--追加 --> {% for food in foods %} <p> {{ food.content }}, {{ food.color }}, <a href='{% url "testapp:detail" food.id %}'>詳細</a> </p> {% endfor %} {% endblock %}<!--追加 --> 3・index.htmlファイルの共通部分をタグで囲む {% extends './base.html' %}・・・どの共通テンプレートを使用するのか決定 {% block content %} ・・・・・・・・・・・ {% endblock %}     ・・・・block contentとendblockで囲まれた部分は共通テンプレートファイルのblock contentとendblockで囲まれた部分に一致する!!! $ python manage.py runserver 4・確認
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む