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

【Python】HTTP通信でGETを直接書く(proxy経由)

はじめに 前の投稿に続いて、今回はproxy を通すところを試してみました。これがやりたかった。 内容 まず、適当なサーバに対してGET を送り、レスポンス(短いのがいいな)が返ってくるの確認します。 $ curl http://google.com <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>301 Moved</TITLE></HEAD><BODY> <H1>301 Moved</H1> The document has moved <A HREF="http://www.google.com/">here</A>. </BODY></HTML> これをproxy経由にしてみます。 無料のproxy server Google 先生に free proxy サーバと問い合わせてみると、いろいろなサイトを教えてくれます。 私は、以下から日本国内にあるものを見つけ、利用させていただくことにしました。 curl でproxy経由させたときの送受信 前回と同様にcURLを使って調べます。proxy server を指定するには、-x を使います。 $ curl -v google.com:80 -x http://116.80.45.7:80 * Trying 116.80.45.7:80... * TCP_NODELAY set * Connected to 116.80.45.7 (116.80.45.7) port 80 (#0) > GET http://google.com:80/ HTTP/1.1 > Host: google.com > User-Agent: curl/7.68.0 > Accept: */* > Proxy-Connection: Keep-Alive > * Mark bundle as not supporting multiuse < HTTP/1.1 301 Moved Permanently < Location: http://www.google.com/ < Content-Type: text/html; charset=UTF-8 < Date: Fri, 08 Oct 2021 13:32:22 GMT < Expires: Sun, 07 Nov 2021 13:32:22 GMT < Cache-Control: public, max-age=2592000 < Server: gws < Content-Length: 219 < X-XSS-Protection: 0 < X-Frame-Options: SAMEORIGIN < <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>301 Moved</TITLE></HEAD><BODY> <H1>301 Moved</H1> The document has moved <A HREF="http://www.google.com/">here</A>. </BODY></HTML> * Connection #0 to host 116.80.45.7 left intact リクエストの部分で、目的とするhost を書いています。このリクエストをproxy server に送っています。 python code で通信してみる なので、同じことをしてみます。コードは前回のを少し変えてこんな感じです。 import socket proxy_hostname, proxy_port = "116.80.45.7", 80 hostname, port = "google.com", 80 server_request = \ f"GET http://{hostname}:{port}/ HTTP/1.1\r\n" \ + f"Host: {hostname}:{port}\r\n" \ + "User-Agent: MyPython\r\n" \ + "Accept: */*\r\n\r\n" print(f"request:\n{server_request}") try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(5.0) s.connect_ex( (proxy_hostname, proxy_port) ) #s.settimeout(None) ret = s.sendall(bytes(server_request, 'utf-8')) except Exception as e: print(f"{e}") raise e try: data = s.recv(8192*16) print( f"response({len(data)}):") print(data.decode('unicode-escape')) except Exception as e: raise e 実行してみると、想定通り。 $ python3 python_proxy_get.py request: GET http://google.com:80/ HTTP/1.1 Host: google.com:80 User-Agent: MyPhthon Accept: */* response(528): HTTP/1.1 301 Moved Permanently Location: http://www.google.com/ Content-Type: text/html; charset=UTF-8 Date: Fri, 08 Oct 2021 13:28:44 GMT Expires: Sun, 07 Nov 2021 13:28:44 GMT Cache-Control: public, max-age=2592000 Server: gws Content-Length: 219 X-XSS-Protection: 0 X-Frame-Options: SAMEORIGIN <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>301 Moved</TITLE></HEAD><BODY> <H1>301 Moved</H1> The document has moved <A HREF="http://www.google.com/">here</A>. </BODY></HTML> 無事に動いてます。やったー、今日は調子がいいぞ。 Proxy tunnel を通す(CONNECT メソドの登場) ここで、curl のオプションに --proxytunnelを付けてみます。 $ curl -v google.com -x http://116.80.45.7:80 --proxytunnel * Trying 116.80.45.7:80... * TCP_NODELAY set * Connected to 116.80.45.7 (116.80.45.7) port 80 (#0) * allocate connect buffer! * Establish HTTP proxy tunnel to google.com:80 > CONNECT google.com:80 HTTP/1.1 > Host: google.com:80 > User-Agent: curl/7.68.0 > Proxy-Connection: Keep-Alive > < HTTP/1.1 405 Method Not Allowed < Content-Type: text/html; charset=UTF-8 < Referrer-Policy: no-referrer < Content-Length: 1592 < Date: Fri, 08 Oct 2021 13:17:05 GMT < Connection: close < * Received HTTP code 405 from proxy after CONNECT * CONNECT phase completed! * Closing connection 0 curl: (56) Received HTTP code 405 from proxy after CONNECT 先程のpython script でリクエストヘッダを書き換えます。 server_request = \ f"CONNECT {hostname}:{port}/ HTTP/1.1\r\n" \ + f"Host: {hostname}:{port}\r\n" \ + "User-Agent: MyPython\r\n" \ + "Accept: */*\r\n\r\n" とりあえず同じものを送れるようにはなり、同じ結果が返ってきました。 まとめ HTTP Proxy を理解するために、curl で送受信されるデータを見て、同じ内容をpython で送ってみた。 とりあえず、これでproxy を通すベタな実装はできる気がした。 本当は、おじさんは便利なモジュールを使いたいのだ。そうなのだが誰も教えてくれないので、自分で実装しているのです。 (2021/10/08)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AI・機械学習入門】よく使うWindowsショートカットキー

はじめに 今回は、Windowsのショートカットキーについてです。 いきなり多くのショートカットキーを詰め込んでも、最初は大変なので、よく使うショートカットキーに絞りこんで説明させていただきます。 Word, Excelといったアプリケーションのショートカットキーはこちらでは紹介しませんので、ご注意ください。 環境 今回紹介するショートカットキーの動作環境は以下の通りです。 Windows 10 ショートカットキーとは メニューからコマンドを選択するような操作の代わりに、キー入力で行う場合の、キーの組み合わせのことです。 これを使ったキー入力をショートカット入力やキーボードショートカットとよびます。 Windowsのショートカットキー一覧 ショートカットキーの一覧については、 Windows10 ショートカットキー 一覧などで検索するとたくさん出てきますので、そちらをご参照ください。 以下に、Microsoftが紹介しているリンク先を添付させていただきます。 Microsoftのショートカット一覧 Windowsでよく使うショートカットキー 今回紹介するよく使うショートカットキーは以下の通りです。 ショートカットキー 内容 Ctrl + A すべての項目を選択する Ctrl + C 選択した項目をコピーする Ctrl + X 選択した項目を切り取る Ctrl + V 切り取り、コピーした項目を貼り付ける Ctrl + S ファイルを保存する Ctrl + Z 操作を1つ前に戻す Ctrl + Y 戻した操作をやり直す F2 ファイル、フォルダーの名前を変更する Windows + D デスクトップを表示する Alt + Tab アプリ、ウィンドウを切り替える 覚える順番のオススメ ショートカットキーは、いきなりすべてを詰め込むのではなく、実際に使ってみて徐々に慣れていくと自然に覚えます。 はじめのうちは、3つくらいを1日打つ練習をすると、すぐに慣れると思いますよ。 Ctrl + A, Ctrl + C, Ctrl + X, Ctrl + V, Ctrl + S よく使うキーなので、まずはこちらのショートカットキーを抑えておくことをオススメします。 Ctrl + Z, Ctrl + Y, F2 次に抑えておきたいショートカットキーです。 プログラムを書くと、ファイルやフォルダを作成したり、入力したキー操作を1つ戻したい場合が出てくるので、重宝します。 Ctrl + Zで誤って操作を戻してしまった場合は、Ctrl + Yで元に戻します。 Windows + D, Alt + Tab ブラウザで資料を見ながら、ターミナルでコマンド入力をしたり、エディタ1 でコーディングをしたりと、プログラミングにおいては、複数のアプリやウィンドウを表示することが多いです。 そんなときに、こちらのショートカットキーで、ウィンドウを切り替えたり、デスクトップを表示したりしますので、覚えておくと便利です。 さいごに 前回の記事は非常にたくさんの方々に見ていただき、ありがとうございます。非常にうれしいです!! 次回は、Google Colab のショートカットキーについて説明します。 また、番外編としてどこかのタイミングで、Pythonのエディタ1 である、Visual Studio Code と PyCharm の2点を紹介したいと考えています。 エディタとは、データの作成や編集を行うためのソフトウェアのことです。Pythonを扱うことのできる有名なエディタには、Visual Studio CodeやPyCharmといったものがあります。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AI・機械学習入門】よく使う Windows のショートカットキー

はじめに 今回は、Windowsのショートカットキーについてです。 いきなり多くのショートカットキーを詰め込んでも、最初は大変なので、よく使うショートカットキーに絞りこんで説明させていただきます。 Word, Excelといったアプリケーションのショートカットキーはこちらでは紹介しませんので、ご注意ください。 環境 今回紹介するショートカットキーの動作環境は以下の通りです。 Windows 10 ショートカットキーとは メニューからコマンドを選択するような操作の代わりに、キー入力で行う場合の、キーの組み合わせのことです。 これを使ったキー入力をショートカット入力やキーボードショートカットとよびます。 Windowsのショートカットキー一覧 ショートカットキーの一覧については、 Windows10 ショートカットキー 一覧などで検索するとたくさん出てきますので、そちらをご参照ください。 以下に、Microsoftが紹介しているリンク先を添付させていただきます。 Microsoftのショートカット一覧 Windowsでよく使うショートカットキー 今回紹介するよく使うショートカットキーは以下の通りです。 ショートカットキー 内容 Ctrl + A すべての項目を選択する Ctrl + C 選択した項目をコピーする Ctrl + X 選択した項目を切り取る Ctrl + V 切り取り、コピーした項目を貼り付ける Ctrl + S ファイルを保存する Ctrl + Z 操作を1つ前に戻す Ctrl + Y 戻した操作をやり直す F2 ファイル、フォルダーの名前を変更する Windows + D デスクトップを表示する Alt + Tab アプリ、ウィンドウを切り替える 覚える順番のオススメ ショートカットキーは、いきなりすべてを詰め込むのではなく、実際に使ってみて徐々に慣れていくと自然に覚えます。 はじめのうちは、3つくらいを1日打つ練習をすると、すぐに慣れると思いますよ。 Ctrl + A, Ctrl + C, Ctrl + X, Ctrl + V, Ctrl + S よく使うキーなので、まずはこちらのショートカットキーを抑えておくことをオススメします。 Ctrl + Z, Ctrl + Y, F2 次に抑えておきたいショートカットキーです。 プログラムを書くと、ファイルやフォルダを作成したり、入力したキー操作を1つ戻したい場合が出てくるので、重宝します。 Ctrl + Zで誤って操作を戻してしまった場合は、Ctrl + Yで元に戻します。 Windows + D, Alt + Tab ブラウザで資料を見ながら、ターミナルでコマンド入力をしたり、エディタ1 でコーディングをしたりと、プログラミングにおいては、複数のアプリやウィンドウを表示することが多いです。 そんなときに、こちらのショートカットキーで、ウィンドウを切り替えたり、デスクトップを表示したりしますので、覚えておくと便利です。 さいごに 前回の記事は非常にたくさんの方々に見ていただき、ありがとうございます。非常にうれしいです!! 次回は、Google Colab のショートカットキーについて説明します。 また、番外編としてどこかのタイミングで、Pythonのエディタ1 である、Visual Studio Code と PyCharm の2点を紹介したいと考えています。 エディタとは、データの作成や編集を行うためのソフトウェアのことです。Pythonを扱うことのできる有名なエディタには、Visual Studio CodeやPyCharmといったものがあります。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ABC204 A~D問題 ものすごく丁寧でわかりやすい解説 python 灰色~茶色コーダー向け #AtCoder

ABC204(AtCoder Beginner Contest 204) A~D問題の解説記事です。 灰色~茶色コーダーの方向けに解説しています。 その他のABC解説、動画などは以下です。 A - Rock-paper-scissors x,yがそれぞれ0,1,2の3パターンあるので全てのパターン数は3×3で9パターンです。 それぞれのパターンについてif文で場合分けし、答えを出力します。 入力の受け取り、出力がわからない方は以下の記事を参考にしてください。 【提出】 # 入力の受け取り x,y=map(int, input().split()) # x,yの値で場合分け if x==0 and y==0: print(0) elif x==0 and y==1: print(2) elif x==0 and y==2: print(1) elif x==1 and y==0: print(2) elif x==1 and y==1: print(1) elif x==1 and y==2: print(0) elif x==2 and y==0: print(1) elif x==2 and y==1: print(0) elif x==2 and y==2: print(2) B - Nuts 問題文の条件通りにコードを書けばよいです。 すなわち 10<A[i]ならば(A[i]-10)個を収穫=答えにプラス とすればよいです。 【提出】 # 入力の受け取り N=int(input()) A=list(map(int, input().split())) # 答えの格納用変数 ans=0 # i=0~N-1まで for i in range(N): # A[i]が10より大きければ if 10<A[i]: # A[i]-10をansにプラスする ans+=A[i]-10 # 答えの出力 print(ans) C - Tour BFS(幅優先探索)を使います。 本問についてではないですが、BFSのやり方については動画で詳しく解説しています。ぜひ御覧ください。 都市1,都市2,...都市Nをスタート地点とした時、到達可能な都市の数をそれぞれ数えればよいです。 到達可能な都市を数えるためにBFSを使います。 BFSはグラフを探索する場合に使用するアルゴリズムです。 本問の場合、スタート都市を引数として指定→そこから到達可能な都市の数を返す関数をつくれば楽です。 BFSの具体的な手順は以下です。 (1)各道路の情報を受け取る (2)スタート都市から到達可能な都市の数を数える変数(count)を用意する(初期値は1、スタート都市→スタート都市へ行けるため) (3)訪問済みかどうかを確認するリスト(visited)を用意する(スタート都市は最初に訪問済としておく) (4)キューを用意する (5)キューにスタート都市を入れる。 (6)キューの左端から都市を取り出す(今いる都市:now_city) (7)now_cityから行ける都市(行ける都市:to_city)を確認する (8)to_cityが未訪問ならば  ・スタート都市から到達可能なのでcountにプラス1  ・to_cityを訪問済とする (9)(6)~(8)をキューが空になるまで繰り返す (10)到達可能な都市の数(count)を返す 関数ができたらスタート都市を1,2,3,...として順に関数へつっこみ、到達可能な都市を数えればよいです。 【提出】 # 入力の受け取り N,M=map(int, input().split()) # 道路の情報を格納するリスト connect=[[] for i in range(N+1)] # M回受け取り for i in range(M): # 入力の受け取り A,B=map(int, input().split()) # connect[A]にBを追加 # 都市1から都市2,3,4にいけるならconnect[1]=2,3,4 connect[A].append(B) # dequeをインポート from collections import deque # BFSの関数を用意 # スタート都市を引数→そこから行ける都市の数を返す def BFS(start_city): # 行ける都市を数える変数 # スタート都市→スタート都市には必ず行けるから1 count=1 # 訪問済み都市のリスト # 訪問済みならTrue、未訪問ならFalse visited=[False]*(N+1) # スタート都市は訪問済みにする visited[start_city]=True # キューを用意 que=deque() # キューへスタート都市を追加 que.append(start_city) # キューが空になるまで while 0<len(que): # 今いる都市 now_city=que.popleft() # 今いる都市から行ける都市を順にto_cityへ for to_city in connect[now_city]: # もしto_cityが未訪問なら if visited[to_city]==False: # countにプラス1 count+=1 # to_cityを訪問済みにする visited[to_city]=True # キューへto_cityを追加 que.append(to_city) # 行ける都市の数を返す return count # 答えを格納する変数 ans=0 # x=1~N for x in range(1,N+1): # xをスタート地点としたときに行ける都市の数を順に計算 ans+=BFS(x) # 答えの出力 print(ans) D - Cooking DPを使います。 2つのオーブンをそれぞれオーブンA、オーブンBと呼びます。 オーブンAでいくつかの料理をつくれば残りの料理は全てオーブンBで作らなければなりません。 よってこの問題を言い換えると以下のようになります。 Tの要素を2グループに分けてそれぞれ合計を計算、大きい方をSする。Sの最小値はいくらか。 例として以下の入力を考えます。 N:4 T:1 4 3 2 料理1,料理2,料理3をオーブンAで作ると1+4+3=8分かかります。 料理4はオーブンBで作ります。Tの合計は10ですから、オーブンBを使う時間は(Tの合計)-(オーブンAを使う時間)=10-(1+4+3)=2だという事がわかります。 すべての料理を作るのにかかる時間(=S)は8と2の大きい方、すなわち8です。 例からわかるように(オーブンAを使う時間)と(Tの合計-オーブンAを使う時間)の大きい方がすべての料理を作るのにかかる時間です。 (Aのオーブンを使う時間)はTからいくつかの要素を組み合わせて合計した数です。 ということはTからいくつかの要素を組み合わせて合計したとき、作れる数はなにかがわかれば解けます。 (Tからいくつかの要素を組み合わせて合計した時0,1,2,...は作れるか?を考えます) このように数列の要素をいくつか組み合わせて合計したときに作れる数を探索する問題は「部分和問題」と言います。 「部分和問題」はDPで解くことができます。 DPとは「ある状態までの答えがわかっていれば→その次の状態の答えも出せる」という手続きを何度も行って最終的な答えを出す方法です。 具体的な手順は以下です。 (1)表を作る (2)すぐにわかるところを埋める (3)表の小さい方から答えにたどり着くまで埋める (4)答えを出力する (1)表を作る 『i番目までの数を組み合わせてxを作れるか』を確認する表を作ります。 実装では二次元配列を作ります。名前はdpとします。 行がi、列がxを表します。 T[0]は0番目の数を表します。が、0番目の数なんて存在しません。便宜上0を入れておきます。 (2)すぐにわかるところを埋める すぐに埋まるところは0行目です。 まずdp[0][0]を考えます。dp[0][0]は『0番目までの数を組み合わせて0を作れるか』という意味です。 0番目の数は「0」で、「0」を使えば0は作れますから、ここは○です。 dp[0][1]はどうでしょうか。dp[0][1]は『0番目までの数を組み合わせて1を作れるか』という意味です。 0番目の数は「0」で、「0」だけでは1は作れません。ここは×(バツ)です。 同様の理由でdp[0][2],dp[0][3],...も×(バツ)です。 (3)表の小さい方から答えにたどり着くまで埋める まず1行目を考えましょう。 dp[1][0]は『1番目までの数を組み合わせて0を作れるか』という意味です。 注意が必要なのは「1番目の数を組み合わせて」ではなく「1番目までの数を組み合わせて」というところです。 1番目の数は「1」ですが、必ずしも組み合わせに使う必要はありません。 1番目までの数ということは0~1番目の数、すなわち「0」と「1」が使えるという意味です。 0だけを使えば「0」は作れますから、dp[1][0]は○です。 dp[1][1]も○です。dp[1][1]は『1番目までの数を組み合わせて1を作れるか』という意味です。 1番目の数である「1」を使えば1を作れます。 1行目は以下のようになります。 次に2行目を考えましょう。 dp[2][0]は『2番目までの数を組み合わせて0を作れるか』です。 dp[1][0]が○、すなわち『1番目までの数を組み合わせて0を作れるか』が○だったわけですから、当然作れます。 dp[2][0]は○です。 dp[2][1]は『2番目までの数を組み合わせて1を作れるか』です。 dp[1][1]が○、すなわち『1番目までの数を組み合わせて1を作れるか』が○だったわけですから、こちらも当然作れます。 dp[2][1]も○です。 よくよく考えると『(i-1)番目までの数を組み合わせてxを作れるか』が○であれば、『i番目までの数を組み合わせてxを作れるか』も当然○になることがわかります。 ((i-1)番目までの数を組み合わせてxを作った方法がそのまま使えるため。「i番目の数を組み合わせて」ではなく「i番目までの数を組み合わせて」であることに注意してください) 一般に考えると以下のようになります。 dp[i-1][x]が○→dp[i][x]も○ dp[2][3]は×(バツ)です。 (2番目までの数=0,1,4の組み合わせでは3を作れません) dp[2][4]はどうでしょうか。 注目すべきはdp[1][0](『1番目までの数を組み合わせて0を作れるか』)が○であることです。 で、あれば2番目の数「4」を「1番目までの数で0を作った組み合わせ」に追加すれば4ができます。 ゆえにdp[2][4]は○です。 具体的に何をしているか説明しますと、 1番目までの数=0,1で0は作れます。(0番目の数「0」を使う) 2番目の数「4」を組み合わせに追加すれば4が作れます。(0番目の数「0」と2番目の数「4」を組み合わせる) もうひとつ例をあげましょう。いま途中まで表が埋まっており、次に黄色の箇所(dp[3][7])が○か×(バツ)か考えています。 dp[3][7]は『3番目までの数を組み合わせて7を作れるか』です。 3番目の数は「3」です。 dp[2][4](『2番目までの数を組み合わせて4を作れるか』)は○です。 2番目までの数を組み合わせて4を作った組み合わせに3番目の数「3」を追加すれば7が作れます。 ゆえにdp[3][7]は○です。 一般に考えると以下のようになります。 dp[i-1][x-T[i]]が○→dp[i][x]も○ ただしx-T[i]がマイナスになると表をはみ出すので、0<=x-T[i]という条件が付きます。 まとめましょう。 ・dp[i-1][x]が○→dp[i][x]も○ ・dp[i-1][x-T[i]]が○→dp[i][x]も○(0<=x-T[i]) 実装ではi=1~N、x=0~(Tの合計)と二重ループを回すことで表を全て埋めることができます。 (4)答えを出力する 表をすべて埋めると以下のようになります。 最後の行は『N(=4)番目までの数を組み合わせてxを作れるか』を表します。 「N番目の数まで」=「Tの要素全て」です。つまりTの要素をいくつか組み合わせて合計したとき、作れうる数が表からわかります。 あとはN行目が○のxそれぞれについて、xと(Tの合計-x)を計算していきます。これらの大きい方が料理をすべて作るのにかかる時間です。 そのうち一番小さいものを出力すればOKです。 pythonだとTLEするのでpypyで提出します。 【提出】 # pypyで提出 # 入力の受け取り N=int(input()) # Tは先頭に[0]を追加して受け取り T=[0]+list(map(int, input().split())) # Tの合計 T_sum=sum(T) # 表を作る # 『i番目までの数を組み合わせてxを作れるか』 # 例:『0番目の数(=0)を組み合わせて「0」を作れる』→dp[0][0]==True dp=[[False]*(T_sum+1) for i in range(N+1)] dp[0][0]=True # i=1~N i番目までの数を組み合わせる for i in range(1,N+1): # x=0~T_sum xを作れるか確認 for x in range(T_sum+1): # もし『(i-1)番目までの数を組み合わせてxを作れる』(→dp[i-1][x]=True)ならば if dp[i-1][x]==True: # 『i番目までの数を組み合わせてxを作れる』(→dp[i][x]=True) dp[i][x]=True # もし0<=x-T[i] かつ 『(i-1)番目までの数を組み合わせてx-T[i]を作れる』ならば if 0<=x-T[i] and dp[i-1][x-T[i]]==True: # 『i番目までの数を組み合わせてxを作れる』(→dp[i][x]=True) dp[i][x]=True # 答えを格納する変数(初期値はバカでかい数) ans=10**20 # x=0~T_sum for x in range(T_sum+1): # 『N番目までの数を組み合わせてxを作れる』ならば if dp[N][x]==True: # max(x,T_sum-x)が料理をすべて作るのにかかる時間=答えの候補 # それまでのansより小さければ更新 ans=min(ans,max(x,T_sum-x)) # 答えの出力 print(ans) 【広告】 「AtCoder 凡人が『緑』になるための精選50問詳細解説」 AtCoderで緑になるための典型50問をひくほど丁寧に解説した本(kindle)、pdf(booth)を販売しています。 値段:100円(Kindle Unlimited対象) 【kindle】 【booth(pdf)】 1~24問目まではサンプルとして無料公開しています
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

個人的pythonまとめ

1.1 np.newaxisで新たな次元を追加 下のようなtrain_Xを行ごとにユークリッドノルムで正規化したい pixel1 pixel2 pixel3 pixel4 pixel5 pixel6 pixel7 pixel8 pixel9 pixel10 ... pixel775 pixel776 pixel777 pixel778 pixel779 pixel780 pixel781 pixel782 pixel783 pixel784 57050 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 66119 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2762 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 6182 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 35804 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 49100 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 20609 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 21440 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 50057 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 5192 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 56000 rows × 784 columns norm = np.linalg.norm(train_X,ord=2,axis=1) #norm.shape : (56000,) train_X_normalizer = norm[:,np.newaxis] #train_X_normalizer.shape = (56000, 1) train_X = train_X/train_X_normalizer #(56000, 784)/(56000, 1) で行ごとに同じ数での割り算 train_X.loc[0][train_X.loc[0]>0] #1つ目のデータで正規化されていることを確認 ポイントは、2行目のnorm[:,np.newaxis]でnormの軸を一つ増やしていること。 1.2 dataframe.loc[0].valuesとdataframe.values[0] データフレームtest_Xが下のようになっているとき、test.loc[0].valuesとtest.values[0]には注意が必要! pixel1 pixel2 pixel3 pixel4 pixel5 pixel6 pixel7 pixel8 pixel9 pixel10 ... pixel775 pixel776 pixel777 pixel778 pixel779 pixel780 pixel781 pixel782 pixel783 pixel784 6670 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 49567 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 50796 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 22310 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 54037 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 35736 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 45283 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1541 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 51612 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 28646 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 14000 rows × 784 columns test.loc[0].valuesはデータ6670をndarrayにしたものになるが、test.loc[0].valuesはデータ0をndarrayにしたものになる。もしデータ0がtestに含まれていなければエラーになってしまうので注意。 1.3 np.savezでnumpy配列を保存 np.savez(outfile, x=x, y=y) #outfile.npzにx,yを保存 npzfile = np.load(outfile) #np.loadで読み込み → npzfile['x']でxにアクセス可能 複数のファイルで開発するときに使えそう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GPUを活用した機械学習ツールNVIDIA RAPIDSをArch Linux + CUDA11.4 でビルドした(Ver21.10版) (2) CUML, CUSIGNAL

前置き GPUを活用した機械学習ツールNVIDIA RAPIDSのビルド方法。今回は最新版=21.08)での構築方法を紹介。Arch Linux の環境では、CUDA11.4.2 + GCC11 となっているのですが、一応ビルドできて、テストも数値エラーやメモリエラーは起きるが一応最後までまわる。 やったこと=RAPIDS のビルド データ処理、機械学習のフレームワークRAPIDSを Arch Linux でビルドした。ただし、ビルドまでにエライ手間がかかったので、皆さんへの共有も兼ねて 0. 基本構成その0 ・・・ Cupyのビルド方法についてはこちらの記事で紹介してます。 1. 基本構成その1 ・・・ こちら。RMM, cuDFというRAPIDS の基本コンポーネントの一部のビルド手順。 2. 基本構成その2 ・・・ いまここ。CUMLというRAPIDS の基本コンポーネントの一部のビルド手順。ついでに、cuSignal という信号制御ライブラリも RAPIDSについて RAPIDS って何?(再掲) NVIDIAが中心となってとりまとめている、GPUを使ったデータ処理&機械学習のフレームワークツールのスイート。Rapidsaiから提供。何が出来るかについては、例えば石黒さんの記事とか参照のこと。 主なコンポーネント RMM(RAPIDS Memory Manager)...GPU上での分散メモリ処理のためのライブラリ CUDF(DataFrame library)...データ前処理・管理のためのライブラリ(Pandas に相当) CUML(Machine Learning library)...機械学習のためのライブラリ(SciPy, SciKit-Learn に相当)。ただしAPI一覧のとおり、かなりのアルゴリズムがManifold関連中心に充実しているのに対して、scikit-learn に存在するAPIが未実装だったりするのでまだまだ発展形だったりする。 CuPy...Preferred Networksによって開発されている線形代数を中心とした数式計算処理ライブラリ群(Numpyに相当)。RAPDISのコンポーネントでは無いが、大きく依存している ビルド方法 GitHubリンクに、PKGBUILDとかビルド順とか。で済ませたいですが、補足説明を。あと参考までにビルドの依存関係をつけます。 事前想定 NVIDIA GeForce RTX/GTXを持っている人 CUDA含めた開発環境が揃っている。 Arch Linux の使い手。EndeavourOSなど派生ディストリビューションでもOKだが、Manjaro のように独自リポジトリを持ち、カーネル、ドライバなどのリリースのペースが異なるものは関知しません。 yay とか PKGBUILD とかが何やってるかが、ある程度わかること。 ビルド順 0. RMM/CUDFビルド記事のとおり、Python CUDF までインストールが終わっていることを想定。CUMLのビルドには、1〜3がいずれも(多分)必要。また、Cupyビルド記事にあるCupyもインストールが終わっていることが必要 1 treelite Distrubuted/Deep Machine Learning Community (DMLC) から提供されるDecision Tree 探索用ライブラリ。ライブラリ+ヘッダファイル(DMLCも含む)にくわえてPythoパッケージも生成(python-treelite) 2 faiss FaceBookがレコメンデーション機能などに提供している近傍検索ライブラリ。ライブラリ+ヘッダだけでなくPythonパッケージも生成される。 3cumlprims NVIDIAからプロプライエタリで提供されるライブラリ。CUMLのビルドに必須。現時点ではCondaパッケージの形式で提供されている。Condaパケージだが、EarlyAccess用にNightly Build版もある。 4CUML RAPIDSが提供する各種計算ライブラリ。21.08 をインストール。ライブラリ+ヘッダファイルが生成される。 5 openucx Unified Communication Xは、Arch Linux の AURから取得してください。 6 python-ucx UCXのPythonラッパ。GPU間で通信を行う場合に必要らしい(シングルGPUでも使える) 9python-CUML CUMLのPythonラッパ。 ここまでビルドして以下が確認できればOK。 $ python Python 3.9.6 (default, Jun 30 2021, 10:22:16) [GCC 11.1.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import cuml >>> その後、テスト実施 事前に、scikit-learn や、今回のリポジトリから提供されている1パッケージ、python-umapもインストールしておく。 $ cd src/cuml/python/cuml/test/ $ pytest -v . とやって、一通りテストが回れば成功。筆者の環境ではテスト(--run_streess/--run_quantity/--run_unit は付けてない)は一通り回る。が、一部項目では計算結果が想定と一致せずFAILとはなっている。 10(オプション) cuSignal Cupy (とCUDA)が入ってたらビルドできます。 記録 (2021.10.8 22:18 JST)...Rapidsai 21.10初版 おことわり この記事は、筆者以外の方の環境でも同様に成功することを保証するものではありません。ビルドや動作確認の失敗(ないとは思いますが)環境が破壊されても責任は一切負いかねます(基本的には自己責任でお願いします。)。 今回紹介したリンク https://github.com/gdaisukesuzuki/PKGBUILD_rapidsai2110
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】HTTP通信でGETを直接書く

はじめに HTTP 1.1 の通信でGETするのをsocket 通信として実装したいと思いました。 で「どんなメッセージを送ればよいのか」を調べたのですが、意外と良く分かりませんでした。 なのですが、それが簡単にわかる方法を見つけたので、ここにメモリます。 行ったこと: HTTP GETリクエストを送られるデータを確認した HTTPサーバにpython でGET を送ってみた 実験 GET request とその response を見る ローカルにHTTPサーバを立てます。 まず、何か書いたテキストファイルを用意します。 . └── hello.txt このディレクトリでHTTP サーバを立てます。http.server については、こちらに使い方の説明がありますが、bind するアドレス、listen するport、ディレクトリなど指定できます。 $ python3 -m http.server これに対して、curl を -v オプションを付けて実行します。 $ curl -v localhost:8000/hello.txt * Trying 127.0.0.1:8000... * TCP_NODELAY set * Connected to localhost (127.0.0.1) port 8000 (#0) > GET /hello.txt HTTP/1.1 > Host: localhost:8000 > User-Agent: curl/7.68.0 > Accept: */* > * Mark bundle as not supporting multiuse * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Server: SimpleHTTP/0.6 Python/3.8.10 < Date: Thu, 07 Oct 2021 15:46:15 GMT < Content-type: text/plain < Content-Length: 10 < Last-Modified: Thu, 07 Oct 2021 15:45:10 GMT < hello!! * Closing connection 0 これで、送受信されるメッセージが見れました。 Python でGET request TCP client でこのメッセージを送ってみます。 python で書くTCP client は以下の通りです。socket でsend/recv するのは binary array です。文字列との間の変換には decode, encode が必要なので注意。 import socket hostname, port = "127.0.0.1", 8000 server_request = \ "GET /hello.txt HTTP/1.1\r\n" \ + "Host: localhost:8000\r\n" \ + "User-Agent: curl/7.68.0\r\n" \ + "Accept: */*\r\n\r\n" print(f"request:\n{server_request}") try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(5.0) s.connect_ex( (hostname, port) ) #s.settimeout(None) ret = s.sendall(bytes(server_request, 'utf-8')) except Exception as e: print(f"{e}") raise e try: data = s.recv(8192*16) print( f"response({len(data)}):") print(data.decode('unicode-escape')) except Exception as e: raise e さて、動かしてみると、、、 $ python3 python_get_client.py request: GET /hello.txt HTTP/1.1 Host: localhost:8000 User-Agent: curl/7.68.0 Accept: */* response(186): HTTP/1.0 200 OK Server: SimpleHTTP/0.6 Python/3.8.10 Date: Thu, 07 Oct 2021 23:02:17 GMT Content-type: text/plain Content-Length: 10 Last-Modified: Thu, 07 Oct 2021 15:45:10 GMT 想定通り、同じレスポンス(当然だ^^;)を得られました。ばんざい。 まとめ HTTP GETのリクエストとレスポンスを見ることができた。 次のステップとして、ここにあるように CONNECT でproxy tunnel を実装してみたいです。 (2021/10/08)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【py2exe】NameError: name 'WinDLL' is not defined

MacやUbuntuで pip install py2exe したり、import py2exeすると NameError: name 'WinDLL' is not defined などたくさんエラーがでる。これはpy2exeはWindows上で動かすことのみを前提としているからである。 つまり、MacやUbuntu上ではpy2exeは動かない。 WindowsのPowerShell上なら動いた。 考えてみると、Windows実行アプリがWindowsが必要なのも納得はいく
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

seabornでページ遷移などを可視化する

はじめに ウェブサービスのユーザがどのページからどのページに移っていっているのかとか、どの工程からどの工程に仕掛品が流れていったのかなどをPythonで可視化したい。 ここでは仮に以下のようなどのページからどのページに移っていったのかについて集計したデータがあったとする。 from to count 0 A A 111 1 A B 1120 2 A C 1230 3 B A 214 4 B B 1225 5 B C 1216 6 C A 327 7 C C 2319 こんな感じでページ→ページの遷移数と遷移確率行列的なものを可視化したい。 実装 以下でpandas, seaborn, matplotlibを使ってJupyter上で可視化する。 import pandas as pd import matplotlib.pyplot as plt import seaborn as sns df = pd.DataFrame({'from': ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C'], 'to': ['A', 'B', 'C', 'A', 'B', 'C', 'A', 'C'], 'count': [111, 1120, 1230, 214, 1225, 1216, 327, 2319]}) df # C→Bのページ遷移は0件とする from to count 0 A A 111 1 A B 1120 2 A C 1230 3 B A 214 4 B B 1225 5 B C 1216 6 C A 327 7 C C 2319 pages = sorted(list(set(df['from'].values) | set(df['to'].values))) pages ['A', 'B', 'C'] count_matrix = pd.DataFrame(index=pages, columns=pages, dtype=float) count_matrix A B C A NaN NaN NaN B NaN NaN NaN C NaN NaN NaN for i in zip(df['from'].values, df['to'].values, df['count'].values): count_matrix.at[i[0], i[1]] = i[2] count_matrix.fillna(0, inplace=True) row_sum = count_matrix.sum(axis=1) row_sum A 2461.0 B 2655.0 C 2646.0 dtype: float64 probabilistic_matrix = count_matrix.div(row_sum, axis=0) probabilistic_matrix A B C A 0.045104 0.455100 0.499797 B 0.080603 0.461394 0.458004 C 0.123583 0.000000 0.876417 sns.heatmap(count_matrix, annot=True, cmap='coolwarm', fmt='g', vmin=0) sns.heatmap(probabilistic_matrix, annot=True, cmap='coolwarm', fmt='.3f', vmin=0, vmax=1) plt.ylabel('from') plt.xlabel('to') plt.show()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pytorch Lightningを使用したEfficientNetのファインチューニング

はじめに EfficientNetをファインチューニングするコードをPyTorch Lightningで実装しました。 画像分類モデルを作成する際の初手として使用することを想定し、ある程度使い回しが効くように実装したつもりですので、ちょっと長いですが最後まで目を通して頂けますと幸いです。 なお、Google Colaboratoryで実行できるnotebookもgitで公開しているので、間違っている点などあれば是非ご指摘いただけますと幸いです。 pytorch_lightning_image_classification.ipynb Efficient-Netとは 2019年当時SoTAを達成した画像認識モデルです。転移学習にも適しているということで、今回はEfficientNetのファインチューニングを行います。 【参考記事】 2019年最強の画像認識モデルEfficientNet解説 PyTorch Lightningとは Pytorchだと頻出しがちな.to(device)がloss.backward()などの定型的な処理やfor文無しでのコーディングが可能となるフレームワークです。また、EarlyStoppingが簡単に実装できる点も個人的には大きなメリットです。 【参考記事】 PyTorch 三国志(Ignite・Catalyst・Lightning) PyTorch Lightning 2021 (for MLコンペ) 環境 Google Colaboratory 使用するデータ pytorchのチュートリアルでも使用されているアリとハチのデータセットを使って分類モデルを作成します。 import os import urllib.request import zipfile url = "https://download.pytorch.org/tutorial/hymenoptera_data.zip" save_path = "hymenoptera_data.zip" if not os.path.exists(save_path): urllib.request.urlretrieve(url, save_path) zip = zipfile.ZipFile(save_path) zip.extractall() zip.close() os.remove(save_path) 事前準備 事前準備として各種インストール、インポートを行います。pytorch-lightningはバージョンによって挙動や引数が結構変わるので、バージョンを指定します。 # 各種インストール(captureはpipの過程を非表示にするため) %%capture !pip install pytorch-lightning==1.4.9 !pip install timm # インポート import glob import random import pickle import numpy as np import pandas as pd import pytorch_lightning as pl import timm import torch import torch.nn as nn import torch.optim as optim from PIL import Image from pytorch_lightning.callbacks import EarlyStopping, ModelCheckpoint from sklearn.model_selection import train_test_split from torch.utils.data import DataLoader, Dataset from torchvision import transforms seedの固定も行います。 # seedの固定 def fix_seed(seed): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed) torch.backends.cudnn.deterministic = True SEED = 0 fix_seed(SEED) 実装の流れ ここからが本格的な実装となりますが、大まかな流れは下記の通りとなります。 LightningDataModuleでモデルに使用するデータ周りを定義 LightningModuleでモデル構造と各Phase(train/valid/test)の挙動を定義 Trainerで学習時の各種設定を定義(Epoch数やEarly Stoppingなど) 学習の実行、精度検証、モデルの保存 1. LightningDataModuleでモデルに使用するデータ周りを定義 自作Datasetの定義 DataModuleを作成する前段階として、自作Datasetのclassを定義します。transformはtrainとvalid/testで異なるためここでは定義せず(trainはaugmentationを実施)、引数として持たせておきます。なお、ラベリング部分は他のデータを使用するときは適宜変更が必要となります。 class MyDataset(Dataset): def __init__(self, file_list, transform=None): self.file_list = file_list self.transform = transform def __len__(self): return len(self.file_list) def __getitem__(self, index): # 画像を読みこんで、指定の方法でtransform img_path = self.file_list[index] img = Image.open(img_path) img_transformed = self.transform(img) # pathに含まれる文字を使用してラベリングを実施 if 'ants' in img_path: label = 0 else: label = 1 return img_transformed, label LightningDataModuleの定義 続いて、LightningDataModuleを継承してDataModuleを作成するためのclassを定義します。 train/valid/testの画像pathリストが引数となっており、fit、testそれぞれのフェーズに応じて必要なDataset、DataLoaderが作成されます。先ほどの自作Datasetを定義した際に後回しにしていたtransformの方法については__init__で定義してます。 class CreateDataModule(pl.LightningDataModule): def __init__(self, train_path, val_path, test_path, img_size=224, mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225), batch_size=16): super().__init__() self.train_path = train_path self.val_path = val_path self.test_path = test_path self.batch_size = batch_size # train時、val/test時の前処理をそれぞれ定義 self.train_transforms = transforms.Compose([ transforms.RandomResizedCrop(img_size, scale=(0.5, 1.0)), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize(mean, std) ]) self.val_test_transforms = transforms.Compose([ transforms.Resize(img_size), transforms.CenterCrop(img_size), transforms.ToTensor(), transforms.Normalize(mean, std) ]) # データのダウンロードなどを行う場合は定義、今回は不要 def prepare_data(self): pass # Trainer.fit()ではtrain/valのDatasetを、Trainer.test()ではtestのDatasetを生成 def setup(self, stage=None): if stage == 'fit' or stage is None: self.train_dataset = MyDataset(self.train_path, self.train_transforms) self.val_dataset = MyDataset(self.val_path, self.val_test_transforms) if stage == 'test' or stage is None: self.test_dataset = MyDataset(self.test_path, self.val_test_transforms) # こちらもTrainer.fit()ではtrain/valのDataLoaderを、Trainer.test()ではtestのDataLoaderを生成 # trainはshuffleあり、val/testはshuffleなし def train_dataloader(self): return DataLoader(self.train_dataset, batch_size=self.batch_size, shuffle=True) def val_dataloader(self): return DataLoader(self.val_dataset, batch_size=self.batch_size) def test_dataloader(self): return DataLoader(self.test_dataset, batch_size=self.batch_size) DataModuleのインスタンスを作成 定義してきたclassを使用してインスタンスを作成します。 train/valid/testそれぞれの画像へのpathが引数となるため、最初にそれぞれのpathリストを作成し、引数として渡します。 # seedを固定 fix_seed(SEED) # valフォルダはtestとして使用 test_path = [path for path in glob.glob("./hymenoptera_data/val/*/*.jpg")] # trainフォルダの画像を7:3でtrain:validに分割 modeling_path = [path for path in glob.glob("./hymenoptera_data/train/*/*.jpg")] train_path, val_path = train_test_split(modeling_path, train_size=0.7) # インスタンスを作成 data_module = CreateDataModule(train_path,val_path,test_path) 2. LightningModuleでモデル構造と各Phaseの挙動を定義 データの準備ができたら、LightningModuleを継承してモデルの定義を行います。 モデル構造の定義 __init__、forwardを定義するところは普通にpytorchで実装する場合とほぼ変わりません。 今回はtimmを使用して学習済みモデルをダウンロードし、classifier部分を付替えることでファインチューニングを実施します。 各Phaseの挙動 def xx_stepで、training/validation/testの各フェーズごとにミニバッチの処理を定義します。関数名をフックに各フェーズで必要となる処理、例えばtraining時のmodel.train()やloss.backward()、validation時のmodel.valid()やtorch.no_grad()といった処理は内部的に行ってくれるため、記述は不要です。全フェーズで必要な.to(device)などの処理も内部的にやってくれます。 class ImageClassifier(pl.LightningModule): def __init__(self, model_name, n_classes, lr=0.0001, criterion=torch.nn.CrossEntropyLoss()): super().__init__() self.save_hyperparameters() # timmで学習済みモデルをダウンロードし、classifier部分を付替え # n_classesにはラベルの件数を渡す(今回はアリとハチの2つなので2) self.model = timm.create_model(model_name, pretrained=True) self.model.classifier = nn.Linear(self.model.classifier.in_features, n_classes) self.lr = lr self.criterion = criterion # 順伝搬 def forward(self, imgs, labels=None): preds = self.model(imgs) loss = 0 if labels is not None: loss = self.criterion(preds, labels) return loss, preds # trainのミニバッチに対して行う処理 def training_step(self, batch, batch_idx): imgs, labels = batch loss, preds = self.forward(imgs=imgs, labels=labels) return {'loss': loss, 'batch_preds': preds.detach(), 'batch_labels': labels.detach()} # validation、testでもtrain_stepと同じ処理を行う def validation_step(self, batch, batch_idx): return self.training_step(batch, batch_idx) def test_step(self, batch, batch_idx): return self.training_step(batch, batch_idx) # epoch終了時にvalidationのlossとaccuracyを記録 def validation_epoch_end(self, outputs, mode="val"): # loss計算 epoch_preds = torch.cat([x['batch_preds'] for x in outputs]) epoch_labels = torch.cat([x['batch_labels'] for x in outputs]) epoch_loss = self.criterion(epoch_preds, epoch_labels) self.log(f"{mode}_loss", epoch_loss, logger=True) # accuracy計算 num_correct = (epoch_preds.argmax(dim=1) == epoch_labels).sum().item() epoch_accuracy = num_correct / len(epoch_labels) self.log(f"{mode}_accuracy", epoch_accuracy, logger=True) def test_epoch_end(self, outputs): return self.validation_epoch_end(outputs, "test") def configure_optimizers(self): optimizer = optim.AdamW(lr=self.lr, params=self.model.parameters()) scheduler = {'scheduler': optim.lr_scheduler.StepLR(optimizer, step_size=5, gamma=0.2)} return [optimizer], [scheduler] モデルインスタンスの作成 上記のクラスを使ってモデルインスタンスを作成します。なお、引数のmodel_nameは下記から選ぶことができます。 https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/efficientnet.py また、今回はアリとハチという2種類の分類モデルとなるため、n_classesは2に設定します。 # モデルインスタンスの作成 model = ImageClassifier(model_name="efficientnet_b0", n_classes=2) 3. Trainerでtrain時の各種設定を定義 TrainerではEarlyStoppingやモデルの保存先、epoch数などを設定します。 # EarlyStoppingの設定 # 3epochで'val_loss'が0.05以上減少しなければ学習をストップ early_stop_callback = EarlyStopping( monitor='val_loss', min_delta=0.05, patience=3, mode='min') # モデルの保存先 # epoch数に応じて、「epoch=0.ckpt」のような形で保存 checkpoint_callback = ModelCheckpoint( filename='{epoch}', monitor='val_loss', mode='min', verbose=True) # trainerの設定 trainer = pl.Trainer(max_epochs=20, gpus=1, callbacks=[checkpoint_callback, early_stop_callback], log_every_n_steps=10) 4. 学習の実行、精度検証、モデルの保存 学習の実行 ここまでインスタンスを作成してきたdata_module、model、trainerを使用して学習を実行します。勝手にループしてくれるので、for文の記述は不要です。また、進捗も自動でいい感じに表示してくれます。 # gpuを設定 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 訓練開始 trainer.fit(model, data_module) 精度検証 学習が終わったら。testデータでモデルの精度検証を行います。test_dataloaderはdata_moduleで定義済みなので、trainer.test()にてtestデータへの当てはめが可能です。今回は明示的に引数で最良時点のモデルを指定しました。 また、学習過程については、モデリング用クラス作成時にself.log()で定義したlossの動きをTensorBoardで確認可能です。 # 精度検証 result = trainer.test(ckpt_path=checkpoint_callback.best_model_path) result # tensorboardでの確認 %load_ext tensorboard %tensorboard --logdir /content/lightning_logs モデルの保存 最後に、モデルの保存を行います。 # 最良モデルの保存 best_model = ImageClassifier.load_from_checkpoint(checkpoint_callback.best_model_path) with open('./best_model.pkl', mode='wb') as fp: pickle.dump(best_model, fp) 参考 【PyTorch×転移学習】学習済みモデルライブラリTIMMのご紹介 関連記事 Pytorch Lightning関連で、過去にBERTの文章分類モデルの実装記事も公開していますので、ご興味のある方はこちらもご参照ください。 Pytorch Lightningを使用したBERT文書分類モデルの実装
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ABC189 C - Mandarin Orange に痺れた

わ、わからん。 解答 please うーん、わかったのかな。 とりあえず、分かったつもりになって書いてみた。 MandarinOrange.py N = int(input()) A = list(map(int,input().split())) def solv(): ans = 0 for l in range(N): ref = A[l] for r in range(l,N): ref = min(ref,A[r]) ans = max(ans,ref*(r-l+1)) print(ans) solv() Python では間に合わないので PyPy にしないと通らないのでご注意ください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

特徴量エンジニアリング

特徴量エンジニアリング 特徴量とは、モデルにインプットする変数のことを言います。 特徴量エンジニアリングとは、手持ちのデータからドメイン知識などを駆使し、新たな特徴量を生成する取り組みのことです。 なぜ特徴量エンジニアリングが必要なのか、それはデータの質を向上させ、より無駄がなく、より意味のあるデータを機械学習のモデルに学習させることで、予測モデルの予測性能(汎化性能)を向上させることができるからです。 ドメイン ドメインとは、手持ちのデータが属する業界や事業等、その領域における専門知識・知見・トレンドなどを表す概念です。 なぜドメイン知識がデータサイエンスで必要となるのか、それはそのデータが生み出される仕組みや背景・経緯を知っていることで、データの構造、特徴や傾向をより深く・広く把握することができるためです。 例えば、IT事業会社のtoCの人材紹介会社のレコメンドシステムのデータを例に取ってみましょう。 マーケティングの基礎知識でもあるCT(CTR)、CPC、CPM、CV(CVR)などの概念を理解していれば、それぞれの数値の関係性を理解することができるでしょう。 またそれにより、広告表示における費用対効果の程度や推移を把握でき、より深い考察をデータから得ることができるでしょう。 このように、ドメイン知識がデータ解析の結果・精度に大きく影響する為、データサイエンティストはドメイン知識の習得・理解が不可欠となります。 データ構造の理解・構造把握 それでは特徴量エンジニアリングのテクニックの解説に入る前に、前提となるデータ理解を深めるテクニック・考え方について解説していきます。 不均衡データ まず初めに、データの全体概要を把握することが寛容です。 具体的には、データの正例・負例の割合や件数を把握すると良いでしょう。 予測したい事象が「頻繁に起こるものなのか」、または「稀にしか発生しない事象なのか」を把握していきましょう。 予測したい事象(正例)が非常に少ないデータを「不均衡データ」と呼びますが、不均衡データの場合は、特別な対応が必要となります。 具体的なアプローチは以下の記事で紹介していますので、是非参考にしてください。 外れ値 次に「外れ値」の有無を把握することも重要でしょう。 外れ値とは、分布から大きく外れた値のことを言います。 「外れ値」はそのままの状態では、モデルの予測結果に想定外の影響を出してしまうものもあり、対処方法を検討する必要があります。 外れ値については、以下で紹介しています! 欠損値 もう一つ、欠損値の確認も必要でしょう。 欠損値とはその言葉の通り、値を持たない(データベース上ではNULLと言う)値のことを言います。 欠損値は、欠損していること自体に意味があるものもあり、また意味のある欠損値から新たに特徴量を生成することも有用です。 特徴量エンジニアリングのテクニック それでは特徴量エンジニアリングのテクニックについて、紹介していきます! 数値変換 数値変換とは、数値(量的)変数を、より説明力の高い、意味のある値に変換する手法です。 またデータ分析の過程では、データの解釈性を高めたり、アルゴリズムに適合する形に変換する目的でも使用されます。 エンコーディング エンコーディングとは、カテゴリ(質的)変数を、数値(量的)変数に変換する手法です。 よりデータの特徴を捉えたエンコーディングを行うことで、質の高いデータセットを作ることができるようになります。 欠損値処理 欠損値処理は、欠損値自体に意味がある場合、また無意味な欠損値が数多くある場合などに対処する手法です。 アルゴリズムによっては欠損値をインプットできないものもあり、事前に前処理が必要です。 欠損値を用いた特徴量生成については、以下で紹介しています! クラスタリング クラスタリングとは、あるデータを特定の規則に乗っ取ってグループ分けする手法です。 クラスター分析などは、データ分析時にもよく使用される手法ですが、以下の記事ではクタスタリングによる特徴量エンジニアリングの方法を紹介しています。 主成分分析 主成分分析とは、より少ない情報に、その手持ちのデータを要約する手法です。 冗長な情報が多く含まれたデータセットでは、データの誤差(ノイズ)をモデルが学習してしまい、正確な予測を行えなくなってしまう危険があります。 ノイズにモデルが適合し過ぎることを過学習(オーバーフィット)と言います。 以下の記事ではその対処方法として「次元削減」の手法を紹介しています。 相互作用特徴量 最後に相互作用特徴量の生成方法について、新たに解説したいと思います。 相互作用特徴量とは、二つ以上の変数をかけ合わせた新しい変数を作る方法のことです。 特に、二つの特徴量の積で表された特徴量を「ペアワイズ交互作用特徴量」と言います。 メリット 二つ以上の特徴量の組み合わせにより、目的変数をうまく表現できる場合、単一の特徴量よりモデルの精度が高まる場合があります。 デメリット 元の項目数が「n個」の場合、特徴量の生成後はの項目数は「nの2乗個」となる為、学習コスト増大する。 これを回避する方法として、特徴選択がありますが、これは別記事で解説とさせていただきます。 以下の結果では、とても若干ですが精度の向上が見られます。特徴量を精査すれば更に改善が可能でしょう。 from sklearn.ensemble import RandomForestClassifier # ランダムフォレスト from sklearn.metrics import accuracy_score, recall_score, precision_score, f1_score # 各評価指標 from sklearn.metrics import confusion_matrix # 混同行列 import sklearn.preprocessing as preproc from sklearn.model_selection import train_test_split def learning(X_train, X_test, y_train, y_test): model = RandomForestClassifier(random_state=42) model.fit(X_train, y_train) y_pred = model.predict(X_test) # 評価 print('Accuracy = ', accuracy_score(y_true=y_test, y_pred=y_pred).round(decimals=3)) print('Precision = ', precision_score(y_true=y_test, y_pred=y_pred).round(decimals=3)) print('Recall = ', recall_score(y_true=y_test, y_pred=y_pred).round(decimals=3)) print('F1 score = ', f1_score(y_true=y_test, y_pred=y_pred).round(decimals=3)) print('='*20) # 混同行列 y_pred = model.predict(X_test) matrix = plot_confusion_matrix(y_pred, y_test) return matrix def plot_confusion_matrix(predict, y_test): pred = np.where(predict > 0.5, 1, 0) cm = confusion_matrix(y_test, pred) matrix = pd.DataFrame(cm) matrix.columns = [['予測_負例(0)', '予測_正例(1)']] matrix.index = [['実際_負例(0)', '実際_正例(1)']] return matrix # 特徴選択 Interaction = df.copy() features = ['残高', 'クレジットカードスコア'] X1 = Interaction[features] y = Interaction['退会区分'] # ペアワイズ交互作用特徴量 X2 = preproc.PolynomialFeatures(include_bias=False).fit_transform(X1) print(f'生成前の列数: {X1.shape[1]}') print(f'生成後の列数: {X2.shape[1]}') print('='*20) # 目的変数の抽出、データ分割 X1_train, X1_test, X2_train, X2_test, y_train, y_test = train_test_split(X1, X2, y, test_size=0.3, random_state=42) # 学習、評価 matrix1 = learning(X1_train, X1_test, y_train, y_test) matrix2 = learning(X2_train, X2_test, y_train, y_test) matrix1 matrix2 まとめ 今回はこれまで解説してきた内容を、系統立てて紹介させていただきましました。 数学や統計的な知識だけでなく、データサイエンティストにとって業務知識に対する深い理解が重要であることを改めて感じました。 今回は取り上げなかった「時系列データ(分析)」や「特徴選択」は別記事で解説していきます。 解析結果 実装結果:github/churn_modeling.ipynb データセット:Churn Modelling classification data set - kaggle 参考資料
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

unitest/jinja2のそれぞれで呼び出される特殊メソッドについて

目的 unittestでは「プログラムが期待通りの構造でデータを返す」を確認する。 そのオブジェクトを識別できる文字列でOK 実際の利用場面(Jinja2)では指定した書式の文字列を渡す。 HTMLタグを使った複雑な文字列 この機能については別途テストケースを用意することで対応したい。 結果 unittest テスト時は __eq__ が利用される。 テストが失敗した時は __repr__ の情報を見せる。 jinja2 __str__ が利用される。 確認 サンプル #!/usr/bin/python import unittest from jinja2 import Template class aclass(object): def __str__(self): return "__str__" def __repr__(self): return "__repr__" def __eq__(self, other): return "cccc" == other a = aclass() tpl_text = 'a = {{ value }}' template = Template(tpl_text) data = {'value': a} disp_text = template.render(data) print(disp_text) test = (1, 2, a, 4) rslt1 = (1, 2, 'bbbb', 4) rslt2 = (1, 3, 'cccc', 4) rslt3 = (1, 2, 'cccc', 4) class testKanaText(unittest.TestCase): def test01_astext(self): self.assertEqual(test, rslt1) def test02_astext(self): self.assertEqual(test, rslt2) def test03_astext(self): self.assertEqual(test, rslt3) unittest.main() 実行結果 $ ./zzz.py a = __str__ FF. ====================================================================== FAIL: test01_astext (__main__.testKanaText) ---------------------------------------------------------------------- Traceback (most recent call last): File "./zzz.py", line 29, in test01_astext self.assertEqual(test, rslt1) AssertionError: Tuples differ: (1, 2, __repr__, 4) != (1, 2, 'bbbb', 4) First differing element 2: __repr__ 'bbbb' - (1, 2, __repr__, 4) + (1, 2, 'bbbb', 4) ====================================================================== FAIL: test02_astext (__main__.testKanaText) ---------------------------------------------------------------------- Traceback (most recent call last): File "./zzz.py", line 31, in test02_astext self.assertEqual(test, rslt2) AssertionError: Tuples differ: (1, 2, __repr__, 4) != (1, 3, 'cccc', 4) First differing element 1: 2 3 - (1, 2, __repr__, 4) + (1, 3, 'cccc', 4) ---------------------------------------------------------------------- Ran 3 tests in 0.002s FAILED (failures=2) 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

unittestではチェック用データを渡しながらも、同じオブジェクトでjinja2へは別のデータを渡したい.

状況 関数の動作確認はデータよりもデータ構造の確認を重視して、jinja2に渡すデータは状況に応じて切り替えたい。前者はオブジェクトの同一性を識別できる文字列を渡し、後者はその文字列を元にしたHTML文字列を渡す。 実現方法 これでイケそう。 unittestでは __ne__ で想定結果データを受け取る。 jinja2へは __str__ でデータを渡す。 サンプル #!/usr/bin/python import unittest from jinja2 import Template class aclass(object): def __str__(self): return "aaaa" def __repr__(self): return "bbbb" def __eq__(self, other): return "cccc" == other a = aclass() tpl_text = 'a = {{ value }}' template = Template(tpl_text) data = {'value': a} disp_text = template.render(data) print(disp_text) b1 = 'aaaa' b2 = 'bbbb' b3 = 'cccc' class testKanaText(unittest.TestCase): def test01_astext(self): self.assertEqual(a, b1) def test02_astext(self): self.assertEqual(a, b2) def test03_astext(self): self.assertEqual(a, b3) unittest.main() 実行結果 $ ./zzz.py a = aaaa FF. ====================================================================== FAIL: test01_astext (__main__.testKanaText) ---------------------------------------------------------------------- Traceback (most recent call last): File "./zzz.py", line 27, in test01_astext self.assertEqual(a, b1) AssertionError: bbbb != 'aaaa' ====================================================================== FAIL: test02_astext (__main__.testKanaText) ---------------------------------------------------------------------- Traceback (most recent call last): File "./zzz.py", line 29, in test02_astext self.assertEqual(a, b2) AssertionError: bbbb != 'bbbb' ---------------------------------------------------------------------- Ran 3 tests in 0.002s FAILED (failures=2) 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonでTorを使ってIPを変える for Windows

前置き どうせ brew か sudo と思ったそこ人!! 注意事項 スクレイピング、Torの使用に関する注意点は各々で確認してください。 実行環境 Windows10 64bit pip 21.2.4 python 3.8.7 32bit その他、記載ないものは全て最新 準備 必須 $ pip install PySocks Torブラウザのダウンロード 以下、ほとんどの人がインストールしているであろうもの $ pip install webdriver-manager $ pip install beautifulsoup4 $ pip install pandas chromedriver.exeのダウンロード Torブラウザを起動して【Always connect automatically】にチェックを入れて、【Connect】をクリックしてください。 ポイント cmdのリダイレクトをsubprocess.callで再現 【args > nul 2>&1】の代わりに【subprocess.call(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)】 Torをヘッドレスモードで実行する 【Tor = f'"..\\Tor Browser\\Browser\\firefox.exe" --headless'】 Seleniumを基本とした3つの方法で確認+α 【Selenium】【Selenium + BeautifulSoup】【urllib.request + BeautifulSoup】【pandasのみ】 以下、コード 宣言 python # coding:utf-8 import subprocess import getpass import time import socks import socket import urllib.request from selenium.webdriver.chrome.options import Options from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from bs4 import BeautifulSoup Tor_start関数 python # ------ Torの起動 ------ def Tor_start(): # 下1行のコメントアウト切り替えで出力プロセスを表示 # subprocess.call(r'taskkill /F /T /IM firefox.exe') # 下1行のコメントアウト切り替えで出力プロセスを非表示 subprocess.call(r'taskkill /F /T /IM firefox.exe', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) Tor = f'"C:\\Users\\{getpass.getuser()}\\Desktop\\Tor Browser\\Browser\\firefox.exe" --headless' subprocess.Popen(Tor) time.sleep(8) Selenium_start関数 python # ------ Seleniumの起動 ------ def Selenium_start(): options = Options() options.add_argument('--headless') options.add_argument('--proxy-server=socks5://127.0.0.1:9150') driver = webdriver.Chrome(ChromeDriverManager().install(), options=options) driver.implicitly_wait(20) driver.get(url) return driver 【driver.get(url)】WebDriverException (note: full exception trace is shown but execution is paused at: ) 上記の個所でエラーが発生した場合は、Torブラウザを起動して【Refresh Tor Browser】を2回クリック、 【Always connect automatically】にチェックを入れ【Connect】をクリックしてください。 メイン処理 python url = 'https://checkip.amazonaws.com/' # ------ IP変更1回目 ------ # ------ Seleniumのみ ------ if __name__ == '__main__': Tor_start() driver = Selenium_start() IP確認1回目 = driver.find_element_by_tag_name('pre').text driver.quit() # ------ IP変更2回目 ------ # ------ Selenium + BeautifulSoup ------ if __name__ == '__main__': Tor_start() driver = Selenium_start() html = driver.page_source soup = BeautifulSoup(html, 'lxml') IP確認2回目 = soup.find('pre').get_text(strip=True) driver.quit() # ------ IP変更3回目 ------ # ------ urllib.request + BeautifulSoup ------ if __name__ == '__main__': Tor_start() socks.set_default_proxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', 9150) socket.socket = socks.socksocket req = urllib.request.Request(url) response = urllib.request.urlopen(req) html = response.read() soup = BeautifulSoup(html, 'lxml') IP確認3回目 = soup.find('p').get_text(strip=True) subprocess.call(r'taskkill /F /T /IM firefox.exe', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) # ------ IP確認 ------ print(IP確認1回目 +'\n'+ IP確認2回目 +'\n'+ IP確認3回目) おまけ python # ------ おまけ ------ # ------ pandasのみ、メイン処理と置き換えると動作 ------ import pandas as pd url = 'https://www.cman.jp/network/support/go_access.cgi' dfs = pd.read_html(url) print(dfs[4].loc[0,1].split('IP')[0]) Tor_start() socks.set_default_proxy(socks.PROXY_TYPE_SOCKS5, '127.0.0.1', 9150) socket.socket = socks.socksocket dfs = pd.read_html(url) print(dfs[4].loc[0,1].split('IP')[0]) 参考にしたサイト Torを使って個人情報をコントロールする方法 PythonでTorを用いてスクレイピングする How to make urllib2 requests through Tor in Python? まいにちプログラミング112日目 Seleniumで毎回IPアドレスを変更
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pyqt5を使って、ボタンを押すと、ダイアログを表示する

コード QtDesigner を使って、画面を作ります。 画面① CountTimer.ui 画面② Input.ui PyUIC を使って、pyファイルに変更します。 main.pyで以下のコードを書きます。 # わかりやすいため、ボタンイベントのコードを略にします import sys import PyQt5 import CountTimer import Input from PyQt5.QtWidgets import QApplication, QWidget from PyQt5.QtCore import QTimer, QTime from PyQt5.QtCore import * # 画面1 class MyClass(QWidget, CountTimer.Ui_Dialog): # 初期化関数 def __init__(self): super(MyClass, self).__init__() self.init_ui() # 画面初期化 def init_ui(self): self.setupUi(self) self.setWindowTitle("計測タイマー") # 画面2 class NewClass(QWidget, Input.Ui_Dialog): def __init__(self): super(NewClass, self).__init__() self.setupUi(self) self.setWindowTitle("Input") def button_set_click(self): self.show() if __name__ == '__main__': app = QApplication(sys.argv) mc = MyClass() nc = NewClass() mc.button_set.clicked.connect(nc.button_set_click) mc.show() sys.exit(app.exec_()) 実行結果 Set button を押すと、
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Django】filter/get/excludeで複数の条件指定やQuerySetを結合する方法

単一の条件指定 通常このように指定していると思います。 views.py #QuerySetで返す MyUser.objects.filter(name=query) #ひとつだけ返す(確実にデータがある場合にのみgetは使う) MyUser.objects.get(name=query) #除外する MyUser.objects.all().exclude(name=query) 複数の条件指定 andの場合 複数の条件全てを満たすものを取得する場合,を使います。 views.py #名前にtaroを含む20歳のユーザーを取得 MyUser.objects.filter(name__icontains="taro", age="20") #名前がtaroで20歳のユーザーを取得 MyUser.objects.get(name="taro", age="20") #名前にtaroを含む20歳のユーザーを除外 MyUser.objects.all().exclude(name__icontains="taro", age="20") orの場合 条件を一つでも満たすものを取得する場合Q objectsと|を使います。 views.py from django.db.models import Q #名前にtaroを含むか、20歳以下のユーザーを取得 MyUser.objects.filter( Q(name__icontains="taro")|Q(age__lte="20") ) #名前にtaroを含むか、20歳以下のユーザーを除外 MyUser.objects.all().exclude( Q(name__icontains="taro")|Q(age__lte="20") ) 上記の場合、taroを含む全てのユーザーと、20歳以下の全てのユーザーが取得できます。 andとor両方使う場合 Q objectsとキーワード引数を混在させる場合は、先にQ objectsを書くこと。 views.py from django.db.models import Q #Tokyo出身で、名前にtaroを含むか、20歳以下のユーザーを取得 MyUser.objects.filter( Q(birthplace="Tokyo"), Q(name__icontains="taro")|Q(age__lte="20")) #キーワード引数を使う場合 MyUser.objects.filter( Q(name__icontains="taro")|Q(age__lte="20"), birthplace="Tokyo",) #これだと無効になる MyUser.objects.filter( birthplace="Tokyo", Q(name__icontains="taro")|Q(age__lte="20")) 全てQ objectsにすれば順番は気にしないでOK! 先にキーワード引数を書くと無効になるので注意! 複数のQuerySetを結合する 複数のQuerySetを結合したい場合は|を使う。 views.py hoge1 = MyUser.objects.filter(hogehoge1) hoge2 = MyUser.objects.filter(hogehoge2) #hoge1と2のQuerySetを結合する total_hoge = hoge1|hoge2 #QuerySetから本人を除外する total_hoge = (hoge1|hoge2).exclude(id=request.user.id) #重複を無くす(※注意) total_hoge = (hoge1|hoge2).distinct() 重複についてshellで確認したところ、hoge1|hoge2の時点で重複が無くります。 なので、クエリが複数のテーブルにまたがっている場合など、何らかの理由で重複が発生した場合以外は使わないでOK! というのも・・ distinctは注意 データベースの種類やorder.byの有無で機能しない場合があります。 PostgreSQLとそれ以外で書き方が変わります。 詳しくは公式ページで確認してみてくださいまし! 演算子で結合 この方法は重複等色々手間が発生するで、上記の|をオススメしますが一応紹介。 views.py hoge1 = MyUser.objects.filter(hogehoge1) hoge2 = MyUser.objects.filter(hogehoge2) #hoge1と2のリスト化して結合する total_hoge = list(hoge1) + list(hoge2) #.excludeは使えないので結合する前に除外しておく hoge1 = MyUser.objects.filter(hogehoge1).exclude(id=request.user.id) hoge2 = MyUser.objects.filter(hogehoge2).exclude(id=request.user.id) #本人を除外した total_hoge = list(hoge1) + list(hoge2) いや、もう演算子を使うのは色々と不便なので却下ですね笑 ちょっと助長でしたがこんな感じです! Mynality オンラインでの仲間づくりに活用できる、心理学を利用したレジュメサービスを作ってます! 自分や色んな人の性格を見ることができますよ! 無料なので使ってみてもらえると嬉しいです!(フィードバックも歓迎です!) Mynalityでは、今回紹介した複数の条件指定を利用して、相性の良いユーザーを表示する機能を実装しています。 ちなみに下のリンクは私のプロフィールです。(性格が見れますw)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

感情を記録するWebアプリケーションを作ってみた

はじめに 初めてWebアプリケーションを作ってみました。グラフをクリックして現在の感情を記録して、後から振り返れるものです。また、1日ごとにどのような感情が多かったかを解析する機能もあります。 開発にはPythonとFlaskを使っています。 開発の経緯 ふと今日は楽しかった1日だったのか、疲れた1日だったのか振り返ろうと思ったけど、大体その日の一番大きな出来事に引っ張られている気がする。そもそも1日の感情の動きがどのようなものだったか忘れてるなと思いました。 そこで、忘れてしまうならアプリケーションとして記録できるとよいと思い「Record Emotion」開発をしました。 Record Emotionの概要 このアプリケーションは自分の感情を記録でき、後から振り返ることができるものです。 画面は下記の3画面から構成されています。 ・感情を記録するトップ画面 ・記録した感情を表示する表示画面 ・日付ごとにポジティブかネガティブのどちらだったかの日付画面 それぞれのページから別の2ページにアクセスするリンクが下部に用意されています。 トップ画面 トップ画面は、最初にアクセスすると表示される画面です。また、感情を記録する入力項目も下部にあります。 感情の記録は、タイトル、グラフへのプロット、コメントの3つを入力した後に「記録する」ボタンを押すことで記録されます。今回は単純な感情(快、不快)ではなく、複雑な感情を記録したかったため、Plutchikの感情の輪を参考にしました。 8つの基本感情から2つを選び組み合わせることで応用感情になるというものです。 詳しいことはPlutchikの感情の輪を参照してください。 入力をして送信すると、入力された情報はタイトル、グラフの軸と座標位置、コメントが送られます。送信が完了すると、自動的に記録した感情を表示する表示画面に遷移します。 記録画面 記録した感情は、表示画面から確認できます。 上部にタイトルと記録した日時を表示し、その下に記録したグラフを描画し、下部にはその時のコメントも合わせて表示します。 技術的にはGoogleSpreadシートを直接読み込み、時系列順に表示しています。 日付画面 日付画面では、1日ごとにその日の感情からポジティブが何件、ネガティブが何件あったのかカウントをしてします。 ポジティブの件数が多い場合には、「今日もよい1日でした!!」と表示します。 ネガティブの件数が多い場合には、「こんな日もある!よく寝よう!」と表示します。 ポジティブとネガティブの件数が同じ場合には、「なんとも言えない日だね」と表示します。 おわりに 初めてWebアプリケーションを作ってみました。 グラフの描画ができないことや入力したデータをどこに保管しておこうか困ったことが多かったですが、 調べているうちにできるかもと試したものをそのまま使っています。 今後はWebブラウザから入力だけでなく、スマートフォンから入力やLINEからアクセスできないかなと考えています。 また、記録したデータもポジティブかネガティブの2択に分けたものであるため、もっとより詳細な解析(24通りの感情分析、時間ごとの感情の変化)について試したいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[簡単爆速]Pythonだけでデプロイ。Streamlit1.0の使い方を紹介

はじめに データ分析の結果や研究成果を公開するとき、多くの方に使ってもらえるように、何かしらツール化したほうがいい場面があります。 Pythonですと、DjangoやFlaskなどを活用することが多いとは思いますが、Streamlitとは、Pythonのみでフロントエンドアプリケーションを作成できるフレームワークです。 何度か紹介してきたStreamlitですが、ついに1.0でGithubを用いてweb上へのデプロイまで簡単にできるようになりました。 Streamlit Cloudの利用方法 2021年10月現在、streamlitcloudを利用するにはinviteしてもらう必要があります。 Freeのところの(Get an invite)からこのページにいき招待メールを受け取りましょう。 数日でメールが返ってきます。loginできたら以下のような画面になるはずです。 GithubにpushしたRepository情報を入力します。環境ファイルは必要なのでrequirement.txtやpyproject.tomlなどを追加しておきましょう。 右側にコンソールもあり、非常に使いやすいUIになっています。 私も昔作った東京のコロナ感染者とワクチン接種者の推移の可視化アプリをこちらに上げてみました。 最後に Streamlitの基礎的な使い方については過去の記事をご覧ください。 1年前は非常にシンプルなアプリしか作れなかったstreamlitですが、多くのウィジェットが追加され、ついにデプロイまで簡単にできるようになりました。今後もアップデートが楽しみです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ABC101 C - Minimization から学んだ

WA がどうしても取れない。 解答を見た。 なるほど、なるほど。ってわかってるのかな?おれ。 ボーっとした後に思い出しながら書いてみた。 なんとか通った。 Minimization.py N,K = map(int,input().split()) A = list(map(int,input().split())) cnt = 0 if K >= N: print(1) else: for i in range(10**6): if K+(K-1)*i >= N: print(i+1) break
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonで数字の桁数が切り捨てされてしまうことへの対処法

問題 Pythonで出力したときに下記のようにe+と表示されて桁数が省略されてしまう。 5.45783032743912e+38 解決策 int型に変えて出力すると全桁出力できた。 print(ans) intAns = int(ans) print((intAns)) 5.45783032743912e+38 545783032743912028389476341548725567488
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【FastAPI】テストケース毎に独立したDBデータを使用する(GitHub Actions付き)

ORMと連携した機能のテストを行う際、テストケース(= テスト関数)毎にクリーンなデータベースが欲しい(テストケース間の依存関係が生まれてほしくない)。 この点についていい感じの方法が実現できたためご紹介する。 参考(一部重複内容あり) FastAPIでテスト用のクリーンなDBを作成してpytestでAPIのUnittestを行う 本記事のソースコード:skokado/fastapi-tutorial 環境 Python: 3.8 fastapi==0.68.2 SQLAlchemy==1.4.25 SQLAlchemy-Utils==0.37.8 pytest==6.2.5 factory-boy==3.2.0 アプリケーション準備 ユーザ認証とブログ管理を行う簡単なアプリケーションを用意する。 ※アプリケーション本体のコードは割愛するためリポジトリを参照 ※ディレクトリ構成 . ├── Pipfile ├── Pipfile.lock ├── README.md ├── alembic.ini ├── app │ ├── __init__.py │ ├── core │ │ ├── __init__.py │ │ ├── celery_app.py │ │ ├── config.py │ │ └── security.py │ ├── crud │ ├── database.py │ ├── dependencies.py │ ├── main.py │ ├── middlewares.py │ ├── models │ ├── routers │ │ ├── __init__.py │ │ ├── auth.py │ │ ├── blogs.py │ │ └── users.py │ ├── schemas.py │ ├── tests │ │ ├── ... │ └── utils │ ├── oauth2.py │ └── token.py └── migration │ ├── README │ ├── env.py │ ├── script.py.mako │ └── versions │ ├── ... 本題 データベース準備 DockerコンテナでPostgreSQLを起動しておく $ docker run -d --rm \ --name test_db \ -p 5433:5432 \ -e POSTGRES_USER=app \ -e POSTGRES_PASSWORD=secret \ -e POSTGRES_DB=app \ postgres:13.4 フィクスチャ準備 テスト用データベースを使用する設定をconftest.pyに仕込んでおき、データベースをfixtureとして使用できるようにしておく。 ※こちらのGitHub Issueも参照されたし Separating database for tests and dev · Issue #125 · tiangolo/full-stack-fastapi-postgresql app/tests/conftest.py app/tests/conftest.py from typing import Generator import pytest from sqlalchemy.orm import Session from sqlalchemy_utils import database_exists, create_database from app.core.config import settings from app.database import Base from tests.utils.overrides import TestingSessionLocal, engine @pytest.fixture(scope="function") def db() -> Generator[Session, None, None]: if not database_exists(settings.SQLALCHEMY_DATABASE_URI): create_database(settings.SQLALCHEMY_DATABASE_URI) Base.metadata.create_all(bind=engine) session = TestingSessionLocal() yield session session.close() Base.metadata.drop_all(bind=engine) ポイントは、dbフィクスチャのスコープを"function"としている点。 こうすればテスト関数毎にセッションをクローズ&データベース初期化が行われ、データがクリーンアップされる。 app/tests/conftest.py ... session = TestingSessionLocal() yield session session.close() Base.metadata.drop_all(bind=engine) テスト用データ作成 factory-boyを使いFactoryクラスを作成しておく。 テストケースの中で簡単にインスタンス化できるようにする。 app/tests/factories.py app/tests/factories.py from factory.alchemy import SQLAlchemyModelFactory import app.models.user as models from app.tests.utils.overrides import TestingSessionLocal class UserFactory(SQLAlchemyModelFactory): name = 'skokado' email = 'skokado@example.com' password = 'MyPassw0rd!' class Meta: model = models.User sqlalchemy_session = TestingSessionLocal() テストケース テストケースの中でdbフィクスチャを使用してデータ作成をするのみで良い。 app/tests/routers/test_auth.py app/tests/routers/test_auth.py from fastapi.testclient import TestClient from sqlalchemy.orm import Session from app.main import app import app.crud.users as users_crud from app.tests import factories client = TestClient(app) class TestAuthRouter: def test_ログイン成功(self, db: Session): user_in = factories.UserFactory() users_crud.create(user_in, db) response = client.post( '/api/login', data={ 'grant_type': 'password', 'username': user_in.name, 'password': user_in.password } ) assert response.status_code == 200 app/tests/routers/test_blogs.py app/tests/routers/test_blogs.py from fastapi.testclient import TestClient from sqlalchemy.orm import Session from app.main import app import app.crud.users as users_crud from app.core.security import create_access_token from app.tests import factories client = TestClient(app) client.headers.update({ 'Authorization': 'Bearer {}'.format(create_access_token) }) class TestBlogRouter: def test_list_empty_blogs(self, db: Session): user_in = factories.UserFactory() users_crud.create(user_in, db) # ログインする response = client.post( '/api/login', data={ 'grant_type': 'password', 'username': user_in.name, 'password': user_in.password } ) jwt = response.json() headers = { 'Authorization': f'{jwt["token_type"].capitalize()} {jwt["access_token"]}' } # アクセストークンを使用してAPIリクエスト response = client.get('/api/blog/', headers=headers) assert response.status_code == 200 assert response.json() == []
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GraphQL Clientの使い方

はじめに GraphQLの勉強のため、以下のサイトのfull-stack tutorialをやってみた。 https://www.apollographql.com/docs/tutorial/introduction/ サーバー側はなんとか理解したが、クライアント側がReactベースで全く分からない。 代わりにPythonからGraphQL Clientを使用してみた。 GraphQLサーバーの起動 cd fullstack-tutorial\final\server npm run start http://127.0.0.1:4000/ スキーマ定義 LaunchConnection.cursorは、打ち上げデータに付与されているlaunch_date_unixに対応。 Mission.missionPatchは、打ち上げに対応するアイコン画像情報。大と小があり、デフォルトは大。 User.tokenは、ここではemailをbase64エンコーディングしたもの。 type Query { launches( """ The number of results to show. Must be >= 1. Default = 20 """ pageSize: Int """ If you add a cursor here, it will only return results _after_ this cursor """ after: String ): LaunchConnection! launch(id: ID!): Launch me: User } type Mutation { # if false, signup failed -- check errors bookTrips(launchIds: [ID]!): TripUpdateResponse! # if false, cancellation failed -- check errors cancelTrip(launchId: ID!): TripUpdateResponse! login(email: String): User } type TripUpdateResponse { success: Boolean! message: String launches: [Launch] } """ Simple wrapper around our list of launches that contains a cursor to the last item in the list. Pass this cursor to the launches query to fetch results after these. """ type LaunchConnection { cursor: String! hasMore: Boolean! launches: [Launch]! } type Launch { id: ID! site: String mission: Mission rocket: Rocket isBooked: Boolean! } type Rocket { id: ID! name: String type: String } type User { id: ID! email: String! profileImage: String trips: [Launch]! token: String } type Mission { name: String missionPatch(size: PatchSize): String } enum PatchSize { SMALL LARGE } PythonからGraphQL Clientを使用 以下のライブラリを使用。 https://github.com/graphql-python/gql pip install --pre gql[all] import json from gql import gql, Client from gql.transport.aiohttp import AIOHTTPTransport transport = AIOHTTPTransport(url="http://127.0.0.1:4000/") client = Client(transport=transport) # 打ち上げ予定の一覧を取得 (名前をlaunchesからpageに変更) query = gql(""" query GetLaunches { page: launches(pageSize: 3) { cursor hasMore launches { id mission { name missionPatch(size: SMALL) } } } } """) result = client.execute(query) print(json.dumps(result, indent=2)) { "page": { "cursor": "1605486420", "hasMore": true, "launches": [ { "id": "109", "mission": { "name": "Starlink-15 (v1.0)", "missionPatch": "https://images2.imgbox.com/9a/96/nLppz9HW_o.png" } }, { "id": "108", "mission": { "name": "Sentinel-6 Michael Freilich", "missionPatch": null } }, { "id": "107", "mission": { "name": "Crew-1", "missionPatch": "https://i.imgur.com/BzaSAnx.png" } } ] } } # ログインしてトークンを取得 query = gql(""" mutation LoginUser { login(email: "daisy@apollographql.com") { token } } """) result = client.execute(query) print(json.dumps(result, indent=2)) { "login": { "token": "ZGFpc3lAYXBvbGxvZ3JhcGhxbC5jb20=" } } # ヘッダーを設定したclientを作成 headers = {'authorization': 'ZGFpc3lAYXBvbGxvZ3JhcGhxbC5jb20='} transport = AIOHTTPTransport(url="http://127.0.0.1:4000/", headers=headers) client = Client(transport=transport) # 打ち上げの予約 query = gql(""" mutation BookTrips { bookTrips(launchIds: [67, 68, 69]) { success message launches { id } } } """) result = client.execute(query) print(json.dumps(result, indent=2)) { "bookTrips": { "success": true, "message": "trips booked successfully", "launches": [ { "id": "67" }, { "id": "68" }, { "id": "69" } ] } } # 打ち上げのキャンセル query = gql(""" mutation BookTrips { cancelTrip(launchId: 68) { success message launches { id } } } """) result = client.execute(query) print(json.dumps(result, indent=2)) { "cancelTrip": { "success": true, "message": "trip cancelled", "launches": [ { "id": "68" } ] } } # ユーザーの情報と予約している打ち上げを取得 query = gql(""" query { me { email trips { id mission { name missionPatch } } } } """) result = client.execute(query) print(json.dumps(result, indent=2)) { "me": { "email": "daisy@apollographql.com", "trips": [ { "id": "67", "mission": { "name": "Merah Putih", "missionPatch": "https://images2.imgbox.com/a8/f5/ZgdsrbqW_o.png" } }, "id": "69", "mission": { "name": "SAOCOM 1A", "missionPatch": "https://images2.imgbox.com/66/d2/oVB1ofaZ_o.png" } } ] } } その他 SQLite3では、INTEGER型のカラムに対してPRIMARY KEY制約を設定すると、自動インクリメントとなる。 Sequelize ORMを使用すると、テーブルのカラムにcreatedAtとupdatedAtが追加される。 データベースの更新処理は非同期で実行される。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

欠損データ

この記事の狙い・目的 機械学習を取り入れたAIシステムの構築は、 ①データセット作成(前処理)→ ②モデルの構築 → ③モデルの適用 というプロセスで行っていきます。 その際「データセット作成(前処理)」の段階では、正しくモデル構築できるよう、事前にデータを整備しておくことが求めらます。 このブログでは、その際に問題なることが多い、データの「欠損」とその対処方法について解説していきます。 欠損データとは 欠損データとは、なんらかの原因により、データの取得、登録ができなかったデータのことを言います。 欠損データの発生原因としては「データ登録の際、未入力項目が存在し、未入力を意味する値も登録されなかった」 「システム障害やプログラムのバグにより本来登録されるはずのデータが登録されなかった、または損失してしまった」 などが考えられます。 発生メカニズム 欠損データの発生メカニズムは、下記の3通りに大別できます。 1.Missing Completely At Random: MCAR  完全に"ランダムで"欠損するケース。  例えば、アンケートへの回答漏れ・忘れなど。 2.Missing At Random: MAR  他の項目と関連して"ランダムで"欠損するケース。  例えば、「性別:女性」の[体重]の入力拒否・未回答など。 3.Missing Not At Random: MNAR  その他の項目、またはその項目自体に関連して欠損するケース。  低(高)所得者ほど、未入力拒否・未回答が多いなど。  既婚者のみを対象とする項目への回答など。 欠損データの分類 データ解析時において、欠損データを「無視可能」か「無視不可能」かを分けて解析する必要があります。 「無視可能」とは、欠損データの解析結果と、欠損していなかった場合の解析結果、その両者に違いがない状態のことを言います。 両者に違いがあれば、「無視不可能」となります。 無視可能な欠損データは、欠損データの発生メカニズムが「MCAR」「MAR」の状態を指します。この場合、そのままのデータを解析・使用するか、適切な手法を用いて解析するか決定する必要があります。 無視不可能な欠損データは、「NMAR」の状態を指します。この場合欠損データの発生原因やその影響を分析した上で、欠損データに対する対処手法を決定する必要があります。 対処手法について、詳しくは後述します。 処理手法 欠損データの処理手法としては、主に3通りがあります。 1.削除する 削除手法には、主に2通りの手法があります。 リストワイズ削除法は、欠損データを含む行・列をすべて削除する手法です。 ペアワイズ削除法は、欠損データの少ない列を残し、そこから欠損している行のみを削除する手法です。 リストワイズ削除法は、欠損したデータ量が少ない際は、解析結果への影響が少ない場合があります。 また、欠損データの発生メカニズムがMCAR(完全ランダム)の場合は、欠損データを無視可能となりますが、 MARの場合は、無視不可能(推定結果に偏りが生じる)となる場合があります。 いずれにしろ削除法は、欠損データ量、発生原因を考慮して行う必要があります。 2.補完する 欠損データを何らかの値で補完する手法があります。適切な手法で補完すれば、完全なデータセットに近い解析結果を得ることが期待できます。 手法には、大きく分けて2通りあります。「単一代入量」「多重代入法」です。 単一代入法とは、字のごとく、特定の単一の値を代入する手法です。 具体的には下記の手法があります。 ・平均値代入法 ・比例代入法 ・回帰代入法 ・確率的回帰代入法 ・ホットデック法 ここではホットデック法について解説します。 ホットデック法とは、類似するデータを補完する手法です。 例えば、類似した背景を持つ回答者の値を、欠損データに補完するなどです。 メリットは回答者の属性等を解析結果に反映することができる点ですが、 作業が煩雑になることがあり、また欠損データが多い場合には類似データの割り出しが難しく、解析結果に偏りが生じることもあり、適さない手法になります。   多重代入法とは、1つの欠損データに対し、浮く数の値を代入する手法です。 具体的には、下記の手法があります。 ・EMアルゴリズムによる補完 ・マルコフ連鎖モンテカルロ法 多重代入法を使用するメリットとしては、欠損データが生じる不確実性を考慮した推計を行うことができる点であり、 短所としては、変数の種類や分析の目的に応じて適切な手法を選択しないと、結果にバイアスが小いる点があります。 3.そのまま使用する 統計モデルなどを仮定することで尤度に基づいた解析を行う手法です。 手法としては、「完全情報最尤推定法」などがあります。 注意点 欠損データの解析の際、下記の点に注意する必要があります。 ・欠損データが他の値に置き換わっている場合 数値データの場合、0,99などに補完されているケースがあります。データの大部分、または全てが欠損している場合は、変数自体を削除することも一つの手です。 カテゴリー変数の場合、「欠損」という一つのカテゴリーとして扱うのも選択肢の一つでしょう。 ・欠損に意味がある場合 その意味がわかる適切な値による補完が必要になります。 例えば「1=ON」という値で登録された変数あった場合、欠損データは「0:OFF」として補完することでバイナリ変数として扱うことが可能となります。 それでは実際のソースコードを見ながら、欠損値を確認していきましょう。 プログラム等の実行環境 Python3 MacBook pro(端末) PyCharm(IDE) Jupyter Notebook(Chrome) Google スライド(Chrome) 欠損値の確認方法 import pandas as pd # データ取得 user_table = pd.read_csv("./user_table.csv") # 欠損データ表示 missing_user_table = pd.DataFrame() for column in user_table.columns: missing_user_table[column] = [user_table[user_table[column].isnull()].shape[0]] user_table_drop = user_table.copy() # リストワイズ削除法 user_table_drop = user_table.dropna() rows = [] rows_drop = [] for column in user_table.columns: rows.append(user_table[column].count()) rows_drop.append(user_table_drop[column].count()) df = pd.DataFrame() df['項目名'] = user_table.columns df['削除前件数'] = rows df['削除後件数'] = rows_drop df # ペアワイズ削除法 # 欠損値の多い列を削除 drop_df = user_table.drop(columns=['age', 'country']) # リストワイズ削除法 user_table_drop = drop_df.copy() user_table_drop = drop_df.dropna() df = pd.DataFrame() df['項目名'] = drop_df.columns rows = [] rows_drop = [] for column in drop_df.columns: rows.append(drop_df[column].count()) rows_drop.append(user_table_drop[column].count()) df['削除前件数'] = rows df['削除後件数'] = rows_drop df import matplotlib.pyplot as plt %matplotlib inline # 描画調整 fig,ax=plt.subplots(figsize = (20 , 5)) ax.set_ylabel('密度') plt.legend(loc='upper left') plt.grid() # 代入前 sns.distplot(user_table['income'], label='before', kde=True, bins=50, color='blue') # 代入後 user_table_fillna = user_table.copy() # 平均値代入法 user_table_fillna['income'] = user_table['income'].fillna(user_table['income'].mean()) sns.distplot(user_table_fillna['income'], label='after', kde=True, bins=50, color='red') 上記の例では、欠損データ数が2527件(全体の5%)あり、単一値(incomeの平均値)で補完していまうと、補完した値が突出して(偏りが出て)しまう。より適切な方法で補完する必要があることがわかる。 どのようなシチュエーションで活用しているのかは、「特徴量エンジニアリング」の記事で総括してまとめていきます。 まとめ 今回はデータ分析時の欠損データの取り扱い方法について記載しました。 多くのデータではなにかしらの欠損データを含むケースが多く、その扱い方法もケースバイケースであることから、より多くの手法を学ぶ必要性を再確認しました。 今回は記載していませんが、モデリング時には、アルゴリズムによって欠損データの解釈が異なるものがあり、また欠損データを含んで学習できないもの、学習はできるがより適切に補完した方が精度が高く出るもの等あり、欠損データの扱い方法もケースバイケースである為、別途モデリング時の欠損データの扱い方法を記載します。 参考文献 統計データの補完推計に関する調査 「第2章 調査における欠測の分類と対応」 公的統計における欠測値補定の研究:多重代入法と単一代入法 欠損データ分析--完全情報最尤推定法と多重代入法- 解析結果 実装結果:github/purchase_forecast.ipynb データセットは「コチラ」の「分析を始める前の準備 > データセットの用意」の章にあります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonで任意のアプリケーションを最前面にする

会社ノートPCの狭い画面での業務は非効率極まりないので とりあえず任意のアプリケーションを最前面にする事でお茶を濁すことにしました 拡張用のモニターが欲しいですね はじめに タイトルまんまです。 こんな感じにTkinterでGUIを作って、ウィンドウ名の一部をテキストボックスに入力し、ON/OFFします。 要点 .findを使って部分一致でウィンドウ名を探す SetWindowPosを使う 先にコード SetWinPos.py import win32gui, win32con import tkinter as tk from tkinter import ttk def foreground_on(hwnd, title): name = win32gui.GetWindowText(hwnd) if name.find(title) >= 0: #最前面ON win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE) return def foreground_off(hwnd, title): name = win32gui.GetWindowText(hwnd) if name.find(title) >= 0: #最前面OFF win32gui.SetWindowPos(hwnd, win32con.HWND_NOTOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE) return mainWnd = tk.Tk() mainWnd.title(u"SetWinPos") mainWnd.geometry("250x100") def Button_1(): value = EditBox.get() win32gui.EnumWindows(foreground_on, value) def Button_2(): value = EditBox.get() win32gui.EnumWindows(foreground_off, value) #エントリー EditBox = ttk.Entry() EditBox.insert(tk.END,"メモ帳") EditBox.pack(expand = True, fill = tk.BOTH, padx = 5, pady = 5) #ボタン1 Button1 = ttk.Button(text='最前面ON', command=Button_1) Button1.pack(expand = True, fill = tk.BOTH, padx = 5) #ボタン2 Button2 = ttk.Button(text='最前面OFF', command=Button_2) Button2.pack(expand = True, fill = tk.BOTH, padx = 5, pady = 5) mainWnd.mainloop() 解説らしきこと SetWinPos.py import win32gui, win32con import tkinter as tk from tkinter import ttk ここで必要なライブラリをインポートします ttkの行は必須ではないですが、今回は見た目の好みの問題でttkを使いました。 SetWinPos.py def foreground_on(hwnd, title): name = win32gui.GetWindowText(hwnd) if name.find(title) >= 0: #最前面ON win32gui.SetWindowPos(hwnd, win32con.HWND_TOPMOST, 0, 0, 0, 0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE) return ここについてはこちらのQAのものがベースです。 QAではreturn時にFalseを返していますが、動作はするもののエラーを吐いてしまいますので削除。 SetForegroundWindowでは今回の目的は達成出来ないのでSetWindowPosに変えます。 処理の流れとしては、 GetWindowTextで取得したフォアグラウンドウィンドウの名前から.findで検索をかけ 検索で見つかれば、そのウィンドウをwin32con.HWND_TOPMOST(最前面)に設定。 def foreground_offは、同様にwin32con.HWND_NOTOPMOST(最前面解除)に設定。 SetWindowPosについてはこちらの記事に解説があります。 以降はTKでのGUI構築なので省略します。 おわりに pywin32を使わずにctypesで書ければ、追加ライブラリのインストールなしで動作させられそうですね。 こんな小さなコードでもPyinstallerでexe化したら9.5MBほどになってしまいます。 UPXかけても9.4MB…
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pytorchで物体検出Yolov5を動作させる

はじめに 前回は、SSDのPytorch版を使って自作画像データで学習して物体検出を行った。 https://github.com/amdegroot/ssd.pytorch 今回は、Yolov5のPytorch版を使って自作画像データで学習して物体検出を行う。 https://github.com/ultralytics/yolov5 Yolov5のインストール 参考HPを列挙します ① YOLOv5を使った物体検出 https://www.alpha.co.jp/blog/202108_02 ② YOLOv5+PyTorchを試してみるだけ https://qiita.com/diyin_near_j/items/3fdce6bfadea9085bffa ③ YOLO V5】AIでじゃんけん検出 https://qiita.com/PoodleMaster/items/5f2cc3248c03b03821b8 anaconda3(64bit)がインストールされている環境で Anaconda Prompt3(Anaconda3)を実行してターミナル画面を起動する。 condaでyolo環境を設定し、この環境に切り替えるコマンド (base) C:\Users\fujio>conda create --name yolo5 python=3.9 (base) C:\Users\fujio>conda activate yolo5 (yolo5) C:\Users\fujio> python virsion確認 (yolo5) C:\Users\fujio>python --version Python 3.9.0 (yolo5) C:\Users\fujio> YOLOv3のPyTorchバージョンを開発していたUltralyticsが、GitHubにYOLOv5のリポジトリを公開しています。 そのリポジトリからYOLOv5をダウンロードします。 https://github.com/ultralytics/yolov5 (yolo5) C:\Users\fujio>git clone https://github.com/ultralytics/yolov5 必要なライブラリをインストールします。 (yolo5) C:\Users\fujio>cd yolov5 (yolo5) C:\Users\fujio\yolov5>pip install -r requirements.txt 自作画像データで学習する 画像strobery001.jpgに対して、アノテーションファイルstrobery001.txtを作成する必要があります。 画像収集 学習用のイチゴの画像を100枚集めた アノテーションツールlabellingを入手する アノテーションツールlabellingの実行型ソフト(Windows_v1.8.0.zip)を次のサイトからダウンロードする https://tzutalin.github.io/labelImg/ 解凍したホルダ内のC:\labelling_windows_v1.8.0\labelling.exeを実行する yolo形式のアノテーションファイルを作成する ① 左の形式指定をクリックしてPascalVOCをYOLOに切り替える ② wキーを押してイチゴの部分に四角を描く ③ ラベル候補が小ウィンドウに表示されるが今回はイチゴのみを学習させるので他の候補を削除する 削除するには、C:\labelling_windows_v1.8.0\data\predefined_classes.txt ファイルを空白に書き換える ④ 候補がなくなった状態でstroberyを登録する これで、ラベル番号0でファイルが作成される ⑤ 複数のイチゴに四角を描いてラベル登録した後 save コマンドアイコンをクリックするとstrobery001.txt ファイルが作成される ファイルの中身は下記のようになっている 0 0.283125 0.785857 0.091250 0.165339 0 0.363750 0.780876 0.092500 0.187251 0 0.424375 0.750000 0.111250 0.149402 0 0.530000 0.687250 0.085000 0.167331 0 0.604375 0.558765 0.083750 0.137450 0 0.574375 0.458167 0.073750 0.103586 0 0.319375 0.598606 0.076250 0.137450 0 0.282500 0.672310 0.075000 0.101594 学習準備 学習用画像ファイルとアノテーションファイルと検証用画像ファイルを次のディレクトリに格納する 学習用画像ファイル C:\Users\fujio\yolov5\data\train\images 学習用アノテーションファイル C:\Users\fujio\yolov5\data\train\labels 検証用画像ファイル C:\Users\fujio\yolov5\data\valid\images 次に、dataフォルダ内にdata.yamlを作成します。 data.yamlはデータセットの設定ファイルです。 \Users\fujio\yolov5\data\data.yaml # # yolov5 # └─ data # └─train # | └─images # | | └─strobery001.jpg # | | strobery002.jpg # | | # | └─labels # | └─strobery001.txt # | strobery002.txt # | # └─valid # └─images # └─strobery201.jpg # strobery202.jpg # train: data/train/images # 学習の画像のパス val: data/valid/images # 検証用画像のパス nc: 1 # クラスの数 names: [ 'strobery' ] # クラス名 学習コマンドをプロンプト画面に打ち込む (yolo5) C:\Users\fujio>cd yolov5 (yolo5) C:\Users\fujio\yolov5>train.py --data data.yaml --weights yolov5s.pt --epochs 100 --batch 4 3時間半位で学習が完了すると、runs/train/exp/weightsにlast.pt, best.ptの重みファイルが生成される。 物体検出 前節で学習させたモデルを使用して物体検出をしてみる。 best.ptを重みファイルとして使用するので、best.ptをyolov5ディレクトリの直下へコピーします。 yolov5ディレクトリに入ってdetect.pyを実行する。 USBカメラで物体検出するには引数sourceに0を指定する。 引数weightsにbest.ptを指定して物体検出する。 (yolo5) C:\Users\fujio>cd yolov5 (yolo5) C:\Users\fujio\yolov5>python detect.py --source 0 --weight best.pt カメラがイチゴを検出してる画面 検出速度 0.16秒 6FPS です。 ラベル表示が大きすぎるのでplots.pyのソースをいじりました。 ① 四角の色を赤から緑にし太さも細くします ② ラベル文字を囲む四角は無くします コメントアウトします ③ ラベル文字も小さく、細くしました yolov5/utils/plots.py ## cv2.rectangle(self.im, p1, p2, color, thickness=self.lw, lineType=cv2.LINE_AA) cv2.rectangle(self.im, p1, p2, (0,255,0), thickness=1, lineType=cv2.LINE_AA) if label: tf = max(self.lw - 1, 1) # font thickness w, h = cv2.getTextSize(label, 0, fontScale=self.lw / 3, thickness=tf)[0] # text width, height outside = p1[1] - h - 3 >= 0 # label fits outside box p2 = p1[0] + w, p1[1] - h - 3 if outside else p1[1] + h + 3 ## color=(128, 128, 128) ## cv2.rectangle(self.im, p1, p2, color, -1, cv2.LINE_AA) # filled ## cv2.putText(self.im, label, (p1[0], p1[1] - 2 if outside else p1[1] + h + 2), 0, self.lw / 3, txt_color, ## thickness=tf, lineType=cv2.LINE_AA) ## txt_color=(255, 255, 255) white ## cv2.putText(self.im , label , (p1[0], p1[1] - 2 if outside else p1[1] + h + 2) , cv2.FONT_HERSHEY_SIMPLEX , self.lw / 6 , txt_color , thickness = 1 ) キャプチャは「'q'」を押下で終了できます。何度か繰り返すと割り込めて終了します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pytorch dropout2dについて

0.初めに pytorchのdropout2dはどのように機能しているのでしょうか. model.eval()とすれば機能せずmodel.train()とすればdropoutしてくれますよね. 使用用途はPyTorchでMNIST by @fukuit などがあります. ただ,実際どのようにコードが書かれているのか,追えていなかったため githubを追って確認してみたいと思います. (結論から言いますが,一部モジュールの中身がよくわからなくなってしまったため,そちらに関しては諦めています.(特にcppとpythonの呼び出しに関して)補足等ありましたら,ご教授いただけると幸いです.) (この記事は2021年10月7日,8日に作成しております.) 1. Dropout2d これはimport torch.nn as nnでimportすればnn.Dropout2d()として使用できるのですが,それはtorch.nn.init.pyにてtorch.nn.modules.*をimportするように設定されていることとtorch.nn.modules.init.pyにdropout.Dropout2dをimportするように書かれていることにあります. Dropout2dはtorch.nn.modules.dropout.pyで定義されています. class Dropout2d(_DropoutNd): def forward(self, input: Tensor) -> Tensor: return F.dropout2d(input, self.p, self.training, self.inplace) とあります. 1.0 _DropoutNd こちらは, class _DropoutNd(Module): __constants__ = ['p', 'inplace'] p: float inplace: bool def __init__(self, p: float = 0.5, inplace: bool = False) -> None: super(_DropoutNd, self).__init__() if p < 0 or p > 1: raise ValueError("dropout probability has to be between 0 and 1, " "but got {}".format(p)) self.p = p self.inplace = inplace def extra_repr(self) -> str: return 'p={}, inplace={}'.format(self.p, self.inplace) また,Moduleは from .module import Module であるため,一つ前のdirにあるtorch.nn.moduels.module.pyにて定義されています.しかし,こちらのクラスはとても長く読むと日が暮れてしまうため 概要を記すと,modelを作成する際に親クラスとして設定するように書かれていました. そのため,_DropoutNdやDropout2dはモデルの一種として捉えることができます. 1.1 F Fは from .. import functional as F とあるため,二つ前のディレクトリにあるtorch.nn.functional.pyを見てみましょう. 1.1.0 functional.py ここの1232行目に def dropout2d(input: Tensor, p: float = 0.5, training: bool = True, inplace: bool = False) -> Tensor: if has_torch_function_unary(input): return handle_torch_function(dropout2d, (input,), input, p=p, training=training, inplace=inplace) if p < 0.0 or p > 1.0: raise ValueError("dropout probability has to be between 0 and 1, " "but got {}".format(p)) return _VF.feature_dropout_(input, p, training) if inplace else _VF.feature_dropout(input, p, training) とあります.一つ一つ見てきましょう. また,最後の行を見るとtrainingのmodeにより,inplaceには依存しないようにも見えます... また,self.trainingはtorch.nn.modules.module.pyから eval()やtrain()で変化することがわかります 1.1.0.0 has_torch_function_unary と handle_torch_function この二つは, from ..overrides import ( has_torch_function, has_torch_function_unary, has_torch_function_variadic, handle_torch_function) とあるため,二つ前のディレクトリにあるtorch.overrides.pyの中を見てみましょう. 1.1.0.0.0 has_torch_function_unary(input) しかし残念ながら,ここでは from torch._C import ( _has_torch_function, _has_torch_function_unary, _has_torch_function_variadic, _add_docstr) とありここでも定義がされていないようです. そのため,torch._Cをみてみましょう. しかし,こちらは一般的なモジュールの書き方がなされていなく, __init__.pyi.inの中にある611行目に定義が書いてあるのですが def _has_torch_function_unary(Any) -> _bool: ... # THPModule_has_torch_function_unary で中身はよくわかりませんでした(諦). 1.1.0.0.1 handle_torch_function(dropout2d, (input,), input, p=p, training=training, inplace=inplace) torch.overrides.pyの1308行目から定義が書かれているのですが,こちらはtorchを扱うように定義されていない関数をtorchを扱えるようにする際に使うようです. つまり,これを噛ませることで,torchで扱える関数となるようです. ここから逆算し,コードを見るとhas_torch_function_unary(input)はinputの中身がtorchかどうかをみているように思えます. 1.1.0.1 _VF これは from torch import _VF でimportされているためtorch._VF.pyの中身を見てみることにします.しかし,feature_dropout_()に関する定義は見当たりません. そこでimportされているものを確認してみることにします. import types from typing import Any, List, Sequence, Tuple, Union import builtins とあるため,torch.types.pyを確認してみます. しかし,types.ModuleTypeに該当するものがありません...(?) ただ,これは勘違いで,実はこれに関してはPythonのdefaultのtypesをimportしていました. 実際にtorch.types.pyをimportする場合は.typesとしなければなリませんでしたね. このように,このファイルには公式ドキュメントに用意されているものしかimportしていないことがわかりました. では,どこでfeature_dropout_()は定義されているのでしょうか. 現状の主にわかっていない点は以下の二つです. A : どこてfeature_dropoout_が定義されているか B : どのようにして_VF.feature_dropout_の形で呼び出しているのか 1.1.0.1.A feature_dropout_ を探す旅 候補としてpytorch/aten/src/ATen/native/Dropout.cppの105行目に定義され c++のコードをpythonにimportしてきているようです. Tensor& feature_dropout_(Tensor& input, double p, bool train) { return _feature_dropout<true>(input, p, train); } _feature_dropoutは79行目に ALIAS_SPECIALIZATION(_feature_dropout, true, false) とありました. ALIAS_SPECIALIZATIONは72行目に #define ALIAS_SPECIALIZATION(ALIAS_NAME, IS_FEATURE, IS_ALPHA) \ template <bool inplace, typename... Args> \ Ctype<inplace> ALIAS_NAME(Args&&... args) { \ return _dropout_impl<IS_FEATURE, IS_ALPHA, inplace>(std::forward<Args>(args)...); \ } とあります. そして,_dropout_implは template<bool feature_dropout, bool alpha_dropout, bool inplace, typename T> Ctype<inplace> _dropout_impl(T& input, double p, bool train) { TORCH_CHECK(p >= 0 && p <= 1, "dropout probability has to be between 0 and 1, but got ", p); if (p == 0 || !train || input.numel() == 0) { return input; } if (p == 1) { return multiply<inplace>(input, at::zeros({}, input.options())); } at::Tensor b; // used for alpha_dropout only auto noise = feature_dropout ? make_feature_noise(input) : at::empty_like(input, LEGACY_CONTIGUOUS_MEMORY_FORMAT); noise.bernoulli_(1 - p); if (alpha_dropout) { constexpr double alpha = 1.7580993408473766; double a = 1. / std::sqrt((alpha * alpha * p + 1) * (1 - p)); b = noise.add(-1).mul_(alpha * a).add_(alpha * a * p); noise.mul_(a); } else { noise.div_(1 - p); } if (!alpha_dropout) { return multiply<inplace>(input, noise); } else { return multiply<inplace>(input, noise).add_(b); } } とありました. しかし,c++とpythonの呼び出しに関して,勉強不足の面があるため こちらは別の機会で追えたらと思います. とにかく,開発にあたりc++の勉強周りもする必要があると感じた次第です. Cで作った関数をpythonで呼ぶ by @nabionや 組み込みエンジニアの戸惑い PythonからC言語を呼び出してみる①(Python/C APIを使った場合)が参考になるかもしれません. 1.1.0.1.B どのようにして呼び出しているか こちらに関しては,現状わからなかったため今回は諦めて,次の機会に追っていきたいと思います. 2. 後書き だいぶ投げやりな記事になってしまいましたが,いかがでしたでしょうか.私としては,pytorchのmoduleを追う際には少なくともc++の知識も必要になってくることを実感できてよかったかと思います. ただ,Dropout.cppの中身を読むとにtrainがfalse(0)であれば,そのままinputを返すように伺えたため,実際に作成したモデルがtrainモードにdropoutが機能し,evalモードではdropoutが起動しないことは確認できたため,当初の目的である『どのように機能しているか』はある程度理解できた気がします. 次の機会では,c++とpythonの呼び出し関係について勉強し,こちらの記事の補足ができたらなと思います. 補足等ありましたら何卒よろしくお願い致します.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ポイ活の勧め。海外ポイントサイト・DropGCの紹介。

皆さんはポイ活をしていますか?メディアで見たり聞いたりする機会もあると思います。 ポイ活とはポイント活動の略称です。ポイントを貯めれば、日々の生活をお得にできます。現金化する事も可能で、ポイントには夢が一杯あります!お小遣いが少しでも増えたら嬉しいですよね。 私自身はポイ活を2017年くらいからしています。決して怪しい活動ではないのでご安心を。 では、ポイ活はどうやって始められるのでしょうか。難しい手続きは一切ありません。ポイントサイトに登録するか、アプリを使うだけです。スマホやパソコンで簡単にお小遣い稼ぎができます。 私が登録しているポイントサイトをご紹介します。それは「DropGC」です。 大量広告で高還元の多いDropGC。10ポイント=1円という特殊な設定から「実は大した金額じゃないのでは・・」と疑われて損しているように思います。同じ広告を比較すると平均的にモッピーやげん玉を上回っています。 DropGCの利用に慣れてきたら、注目したいのが友達紹介制度です。家族や知人、ブログやツイッターなどで紹介することができます。紹介するとポイントがついたり、利用してくれた案件の数十%がずっと還元されるシステムです。もちろん友達のポイントが減ることはありません。 とても魅力的なこの制度を生かしてみましょう。 広告量やゲーム量が多く、最も少額の100円から無料で交換できるので、とりあえず換金してみたいなら1番手軽にお小遣いにできます。 0歳以上から登録利用無料です。 どのサイトも登録や利用は無料なので、組み合わせて利用するのもオススメです。高報酬なポイントサイトが多いので、たくさん登録しすぎると使いきれない、ポイントが分散して稼ぎにくいと感じます。DropGC1本を極めるか、メインサイト+比較サイトが少しあると便利です。 この業界の中では珍しく米国に本拠を置く財団が運営しています。 非常に安全性の高いサイトです DropGCでは、全ページでSSL/TLSを導入しています。 SSL/TLSというのは、情報を暗号化して送受信することで第三者から大切な情報を勝手に見られないようにする技術です。 ちなみに、SSL/TLSの導入は、サイトURL「http」の後に「s」があることで確認することができ、DropGCでもバッチリ確認できます。 全然稼げないポイントサイトも多いので、適当に選んでいるとまったく稼げないことが多いです。 しかし、DropGCはしっかり使えば、かなりの金額を稼ぐことも可能です。 https://dropgc.gift 【↑此方のリンクからDropGCに登録可能です】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ポイ活の勧め。海外ポイントサイト・DropGCの紹介。

皆さんはポイ活をしていますか?メディアで見たり聞いたりする機会もあると思います。 ポイ活とはポイント活動の略称です。ポイントを貯めれば、日々の生活をお得にできます。現金化する事も可能で、ポイントには夢が一杯あります!お小遣いが少しでも増えたら嬉しいですよね。 私自身はポイ活を2017年くらいからしています。決して怪しい活動ではないのでご安心を。 では、ポイ活はどうやって始められるのでしょうか。難しい手続きは一切ありません。ポイントサイトに登録するか、アプリを使うだけです。スマホやパソコンで簡単にお小遣い稼ぎができます。 私が登録しているポイントサイトをご紹介します。それは「DropGC」です。 大量広告で高還元の多いDropGC。10ポイント=1円という特殊な設定から「実は大した金額じゃないのでは・・」と疑われて損しているように思います。同じ広告を比較すると平均的にモッピーやげん玉を上回っています。 DropGCの利用に慣れてきたら、注目したいのが友達紹介制度です。家族や知人、ブログやツイッターなどで紹介することができます。紹介するとポイントがついたり、利用してくれた案件の数十%がずっと還元されるシステムです。もちろん友達のポイントが減ることはありません。 とても魅力的なこの制度を生かしてみましょう。 広告量やゲーム量が多く、最も少額の100円から無料で交換できるので、とりあえず換金してみたいなら1番手軽にお小遣いにできます。 0歳以上から登録利用無料です。 どのサイトも登録や利用は無料なので、組み合わせて利用するのもオススメです。高報酬なポイントサイトが多いので、たくさん登録しすぎると使いきれない、ポイントが分散して稼ぎにくいと感じます。DropGC1本を極めるか、メインサイト+比較サイトが少しあると便利です。 この業界の中では珍しく米国に本拠を置く財団が運営しています。 非常に安全性の高いサイトです DropGCでは、全ページでSSL/TLSを導入しています。 SSL/TLSというのは、情報を暗号化して送受信することで第三者から大切な情報を勝手に見られないようにする技術です。 ちなみに、SSL/TLSの導入は、サイトURL「http」の後に「s」があることで確認することができ、DropGCでもバッチリ確認できます。 全然稼げないポイントサイトも多いので、適当に選んでいるとまったく稼げないことが多いです。 しかし、DropGCはしっかり使えば、かなりの金額を稼ぐことも可能です。 https://dropgc.gift 【↑此方のリンクからDropGCに登録可能です】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ポイ活の勧め。海外ポイントサイト・DropGCの紹介。

皆さんはポイ活をしていますか?メディアで見たり聞いたりする機会もあると思います。 ポイ活とはポイント活動の略称です。ポイントを貯めれば、日々の生活をお得にできます。現金化する事も可能で、ポイントには夢が一杯あります!お小遣いが少しでも増えたら嬉しいですよね。 私自身はポイ活を2017年くらいからしています。決して怪しい活動ではないのでご安心を。 では、ポイ活はどうやって始められるのでしょうか。難しい手続きは一切ありません。ポイントサイトに登録するか、アプリを使うだけです。スマホやパソコンで簡単にお小遣い稼ぎができます。 私が登録しているポイントサイトをご紹介します。それは「DropGC」です。 大量広告で高還元の多いDropGC。10ポイント=1円という特殊な設定から「実は大した金額じゃないのでは・・」と疑われて損しているように思います。同じ広告を比較すると平均的にモッピーやげん玉を上回っています。 DropGCの利用に慣れてきたら、注目したいのが友達紹介制度です。家族や知人、ブログやツイッターなどで紹介することができます。紹介するとポイントがついたり、利用してくれた案件の数十%がずっと還元されるシステムです。もちろん友達のポイントが減ることはありません。 とても魅力的なこの制度を生かしてみましょう。 広告量やゲーム量が多く、最も少額の100円から無料で交換できるので、とりあえず換金してみたいなら1番手軽にお小遣いにできます。 0歳以上から登録利用無料です。 どのサイトも登録や利用は無料なので、組み合わせて利用するのもオススメです。高報酬なポイントサイトが多いので、たくさん登録しすぎると使いきれない、ポイントが分散して稼ぎにくいと感じます。DropGC1本を極めるか、メインサイト+比較サイトが少しあると便利です。 この業界の中では珍しく米国に本拠を置く財団が運営しています。 非常に安全性の高いサイトです DropGCでは、全ページでSSL/TLSを導入しています。 SSL/TLSというのは、情報を暗号化して送受信することで第三者から大切な情報を勝手に見られないようにする技術です。 ちなみに、SSL/TLSの導入は、サイトURL「http」の後に「s」があることで確認することができ、DropGCでもバッチリ確認できます。 全然稼げないポイントサイトも多いので、適当に選んでいるとまったく稼げないことが多いです。 しかし、DropGCはしっかり使えば、かなりの金額を稼ぐことも可能です。 https://dropgc.gift 【↑此方のリンクからDropGCに登録可能です】
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む