20210917のPythonに関する記事は22件です。

ABC194 C - Squared Error から学んだ

ふむふむ。サンプルを見てみよう うーん。シグマ使ってるけど、結局、 A の要素の組み合わせで差 => 二乗 で良くね? だがしかし、WA SquaredError_r0.py N = int(input()) A = list(map(int,input().split())) #辞書で要素をリスト dic = {} for i in range(N): if A[i] not in dic: dic[A[i]] = 0 dic[A[i]] += 1 #配列長の N でカウントすると 3*10^5 もあるので #条件の|A|<200 を使って combination すれば良いかと。 score = 0 from itertools import combinations for a,b in combinations(range(-200,201),2): if a in dic and b in dic: score += (a-b)**2 print(score) 結局、条件には A の要素は全て異なる値とは書いていない。 もしかして重複する可能性もあるのか? 解説を聞いたが、自分の力不足なのか、ピンとこない。 有識者の解答を見るとしっくり来るものを見つけた。 結局、重複したことを前提に書き換えると良いみたいだ。 SquaredError_r1.py N = int(input()) A = list(map(int,input().split())) dic = {} for i in range(N): if A[i] not in dic: dic[A[i]] = 0 dic[A[i]] += 1 score = 0 from itertools import combinations for a,b in combinations(range(-200,201),2): if a in dic and b in dic: score += dic[a]*dic[b]*(a-b)**2# <= dic[a], dic[b] の値を掛けた。 print(score) 試しに 1,2,2,1 or 1,2,3,1 で試してほしい。 手書きで全部列挙して計算すると驚きが待っている。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Seabornってすごいのよ!

はじめに 僕は理系大学生として日々Pythonで遊んだり苦戦してたりしてます. そのなかでmatplotlibを使うことがよくあるのですが少しめんどくさい(できることが多すぎて手間が多い)と感じます. なので今回はもっと手軽に使えておしゃれにプロットできる個人的最強ライブラリ"Seaborn"についてです. Seabornとは Seabornとは簡単に言うとPythonで使えて簡単にそれっぽく見せられるグラフ描画ライブラリです 僕も詳しくその概要を説明できるわけではないですが,Seabornはmatplotlibの機能を利用しています.そのためグラフの調整や大まかなルールはmatplotlibと一緒です. インストール方法 !pip install seaborn ※seabornをインストールする際"numpy","matplotlib","pandas"他も同時にインストールします.すでにインストールしている場合,バージョンの違いなどによって実行時エラーが発生する可能性があります(僕はそのエラーが何かわからず1時間ネットの海をさまよった)のでよくわからないエラーが出たら一度上記のライブラリをアップグレードしてみてください. 何ができるの? Seabornは上記の通りmatplotlibをもとに作られています.なのでmatplotlibでできることはすべてできます. さらにタイタニック号のデータなど有名なものはもともと入っているのでデータビジュアライゼーションの学習にはとても適しています. 「Seaborn hogehoge」と調べると大体この中に入っているデータを使っている記事が出てくるのでマネしやすいです 試しに ということで早速Seabornのすごさを体験してみましょう. import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt df = sns.load_dataset("titanic") df.head() dfにはタイタニック号のデータが入っています.冒頭五行を見てみることうなっているはずです.(後半は省略してます.) survived pclass sex age sibsp --- 0 3 male 22.0 1 --- 1 1 famale 38.0 1 --- 1 3 female 26.0 0 --- 1 1 female 35.0 1 --- 0 3 male 35.0 0 --- まずはmatplotlibのほうで描画してみます. plt.bar(x="sex",height="age",data=df) plt.show() これ間違えてます明日更新するのでちょっと待っててください よくあるありきたりなグラフです. これがSeabornだと… sns.set() sns.barplot(x="sex",y="age",data=df,ci=None) plt.show() なんかいい感じにしてくれましたね 疲れたのでこれ以降はまた今度
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

文章を非可逆圧縮するという世紀の大発見!という夢の跡

全文を圧縮した前文 長文なのではじめに結論を置いときますが、最高に頭のネジが飛んでいるポエム(当社比)ができました。 最初と最後だけでも流し読みしていただけると嬉しいです。 結論から言うと文章を非可逆圧縮、冗長展開するとこうなります。 原文: ディレクトリAの内容を親ディレクトリに移動する 非可逆圧縮: 名鑑aの刃を親名鑑に動為す 冗長展開: メモリー(普通ハードディスク)に保存されているファイルのリストローマ字の最初の文字の感じ取ったこと、発見したこと、あるいは学習したことの全体あるいは範囲を子どもを生み出す、出産する、子供を養育して、あるいは育てる人メモリー(普通ハードディスク)に保存されているファイルのリストに新しい位置または場所に動かす、あるいは移動させる、具体的および抽象的な意味でも考慮、判断または使用のために提案する 画像や音声が非可逆圧縮できるのに文章ができない訳がない 画像をJPEGで保存すると、無圧縮のBMPよりサイズが小さくなるじゃないですか。 勘違いを恐れずに詳細を省いて説明を圧縮すると、JPEGの原理は画像を(人間の目では分かりにくい程度に)多少粗くしてサイズを縮小します。 JPEGに変換したファイルをBMPやPNGに再変換しても細かいノイズは消えません。 つまり元の画像には戻せない(非可逆)圧縮が起こるってことです。 ところでZIPファイルなんですけど、これもテキストとかのサイズが小さくなりますね。 マサカリをセルフ投擲しながら説明を圧縮すると、ZIPってPCの中にいるハフマンさんが辞書をランレングすると小さくなるんでしたっけ? そんな感じの仕組み(これをセルフマサカリと言います)で小さくなります! 圧縮の原理 難しい話は良く分かりませんが今回は辞書式圧縮に注目しました。 例えば下の文章があります。 フォルダ1のサブフォルダとしてフォルダ2とフォルダ3、そしてフォルダ4があります。 この文章では『フォルダ』という単語が頻出するのでフォルダ=Fという辞書を定義して書き換えてみましょう。 フォルダ=F;F1のサブFとしてF2とF3、そしてF4があります。 このように同一の単語を辞書化して短く置換することで、テキストを可逆圧縮することが可能です。 元の文章に戻す時は『F』を『フォルダ』に置換すれば完全に元通りです。 今回は13文字分短くなりました。 でもですね、次の文章はどうでしょうか。 フォルダ1のサブフォルダとしてディレクトリ2とフォルダー3、folder4があります。 これを先ほどのように辞書を使って圧縮するとこうなります。 フォルダ=F;F1のサブフォルダとしてディレクトリ2とFー3、folder4があります。 『フォルダ』と『フォルダー』が類似の表現だったのでかろうじて2文字分圧縮できましたが、辞書化による恩恵は微々たるものです。 文章の非可逆圧縮 ここで私に閃光が走りました! 『フォルダー』『ディレクトリ』のような表記揺らぎや類義語をすべてまとめてしまえば文意を損ねることなくテキストを非可逆圧縮できるのではないか!! つまりこういうことです。 【圧縮前】わがはいはにゃんにゃんではありませんにゃ。ネームはいまだに無いんですにょ。名前はナッシングって言い換えることもできるにゃね。 【圧縮後】私は猫では無い。名は未だ無い。名は無いと換言可。 圧縮後の方が圧倒的に短くなり、表記揺らぎを抑えたので『無い』という単語が複数回出てきます。 しかも、文意も損ねてないよね。んー?どう見て損ねてないっしょ。損ねてないって言ってよ。と棍棒を持ったおにいさんに肩をトントン叩かれながら言われたら、きっと文意も損ねてないと同意する人がいるはずです。 文章のフレーバーをすべて吹き飛ばしつつ、意図や個性を表すデジキャラ性も多少損失した気がしますが、棍棒外交に比べれば些細なことですにょ。 日本語Wordnet 類語や表記揺らぎをまとめれば、きっと長文もスッキリ短くなってハフマンさんが辞書をランレングする仕事(これを天丼といいます)が楽になること請け合いです! でも類語データってお高いんでしょう? いいえ、ご心配には及びません。あるんですよ…日本語Wordnet様がね! リンク先を引用します。 国立研究開発法人情報通信研究機構(NICT)では、大規模かつどなたでもご入手いただける日本語の意味辞書を開発することを目的とし、 2006年から日本語ワードネットの開発を進めて参りました。(中略) ライセンスを保持していただければ、基本的に日本語ワードネットは無償で使用、複写、改変、頒布していただけます。詳細はライセンスをご参照下さい。 類語も分かる意味辞書が、無料で作成されているのです。ありがたやありがたや。 セットアップ さっそくセットアップして使いましょう。 日本語Wordnetのダウンロードページから"Japanese Wordnet and English WordNet in an sqlite3 database"みたいなリンクをクリック ダウンロードしたwnjpn.db.gzをWindowsユーザは妙に苦労しながら展開する SQLiteの中身が見れるDBeaverとかDB Browserとか適当なDBクライアントを用意する あとはよしなによろしく SQLで類語取得 Qiitaの素晴らしい記事をオマージュして実際にSQLで類語を取り出します。 with word_sense as (select w.wordid, w.lemma, se.synset from word w join sense se on w.wordid = se.wordid) select luigi.* from word_sense luigi join word_sense src on luigi.synset = src.synset where src.lemma = '猫' order by LENGTH(luigi.lemma) 実行結果 wordid lemma synset 186873 猫 02121620-n 208204 ネコ 02121620-n 97864 cat 02121620-n 181396 ねんねこ 02121620-n 204434 キャット 02121620-n 186221 にゃんにゃん 02121620-n 57508 true_cat 02121620-n wordidが単語IDで、lemmaが単語、synsetが単語の概念グループIDみたいな感じらしいです。 wordidの属するsynsetから同じ概念グループの単語を丸ごと取れました。 「にゃんにゃん」とか「true_cat」とか美辞修辞に使えそうな単語を全部「猫」という一文字に圧縮できちゃいます! 韻律もレトリックも最後にはちゅーるもとい圧縮率にはかなわないんですよ。 pythonで短縮類語取得 これはおまけですが、さっきのSQLをpython 3.9から呼び出して一番短い類義語を取得するサンプルです。 import sqlite3 # SQL文 sql_format = """ with word_sense as (select w.wordid, w.lemma, se.synset from word w join sense se on w.wordid = se.wordid) select luigi.* from word_sense luigi join word_sense src on luigi.synset = src.synset where src.lemma='%s' order by LENGTH(luigi.lemma) """ # 単語から短い類義語を取得する def word_to_luigi(word): with sqlite3.connect("wnjpn.db") as db: cur = db.execute(sql_format % word) row = cur.fetchone() return row[1] if row else word for s in ["ドラゴン  ", "ヨッシー  ", "ムーンライト", "玉兎    "]: print(f"{s} is {word_to_luigi(s.strip(' '))}") 実行結果 ドラゴン   is 龍 ヨッシー   is ヨッシー ムーンライト is 月 玉兎     is 月 ね?簡単でしょ? ドラゴンは類義語の『龍』になります。 ヨッシーが『龍』にならないってことはつまり…スーパードラゴンだから上位種だってことなのです! ムーンライトも玉兎も、概念グループは違えど全部『月』です。 想定通り風情はありませんが、(文脈によって)意味は通らなくもないです。 実践!非可逆圧縮 あとはプログラムで組み合わせるだけです。 でも私はプログラム作るのが苦手なので、その前にきちんと設計をしておきます。 形態素解析する→類語を探す→一番短い単語に書き換える→ビクトリー 完璧な設計ができました! ちなみに形態素解析は何でもいいですが、今回は使ったことのあるJanomeにしました。 この設計を元に作ったソースコードはこちらです。 ソースコードと出力結果 import sqlite3 from janome.tokenizer import Tokenizer # SQL文 sql_format = """ with word_sense as (select w.wordid, w.lemma, se.synset from word w join sense se on w.wordid = se.wordid) select luigi.* from word_sense luigi join word_sense src on luigi.synset = src.synset and luigi.wordid <> src.synset where src.lemma='%s' order by LENGTH(luigi.lemma) """ # 単語から短い類義語を取得する def word_to_luigi(db, token): word = token.surface pos = token.part_of_speech.split(",") #print(token) if pos[0] in ["名詞", "形容詞", "副詞", "動詞"]: cur = db.execute(sql_format % word) row = cur.fetchone() return row[1] if row else word else: return word def compress_text(tokenizer, text): with sqlite3.connect("wnjpn.db") as db: return "".join([word_to_luigi(db, t) for t in tokenizer.tokenize(text)]) text_list = ["Beautiful moon is beautiful ですね。", "あなたは非常に美しいです。", "あなたは非常に美しかったです。", "わたくしのドラゴンはサラマンダーよりも迅速に戦いの庭を駆け抜ける!", "フォルダ1のサブフォルダとしてディレクトリ2とフォルダー3、folder4があります。", "わがはいはにゃんにゃんではありませんにゃ。ネームはいまだに無いんですにょ。名前はナッシングって言い換えることもできるにゃね。", ] tokenizer = Tokenizer() for text in text_list: compressed = compress_text(tokenizer, text) print(f"変更前: {text}") print(f"変更後: {compressed}") print("") 出力結果 変更前: Beautiful moon is beautiful ですね。 変更後: Beautiful 月 is 佳 ですね。 変更前: あなたは非常に美しいです。 変更後: あなたは大に佳です。 変更前: あなたは非常に美しかったです。 変更後: あなたは大に美しかったです。 変更前: わたくしのドラゴンはサラマンダーよりも迅速に戦いの庭を駆け抜ける! 変更後: わたくしの龍はサラマンダーよりも急に戦の庭を駆け抜ける! 変更前: フォルダ1のサブフォルダとしてディレクトリ2とフォルダー3、folder4があります。 変更後: フォルダiのサブフォルダとして名鑑2とフォルダ3、小冊4があります。 変更前: わがはいはにゃんにゃんではありませんにゃ。ネームはいまだに無いんですにょ。名前はナッシングって言い換えることもできるにゃね。 変更後: わがはいはにゃんにゃんではありませんにゃ。名は猶無いんですにょ。名は零って言替える事も可能にゃね。 考察 たしかに短くはなります。 なるけど…なるほど分かりました。 これではまだGAFAMからのオファーには足りません! 今から足りない部分の反省会をします。 大文字 変更前: Beautiful moon is beautiful ですね。 変更後: Beautiful 月 is 佳 ですね。 『moon』と『beautiful』は変換できていますが、意味辞典の単語は小文字なので『Beautiful』が変換できていません。 高みを目指すならもうちょっとがんばりましょう。 活用形 変更前: あなたは非常に美しいです。 変更後: あなたは大に佳です。 変更前: あなたは非常に美しかったです。 変更後: あなたは大に美しかったです。 『大に佳』が日本語として大に佳かどうかはともあれ、活用形をそのまま検索してもヒットしません。 『美しかっ』は形容詞・イ段,連用タ接続なので、活用形を活かしたまま『佳かっ』に変換するとネクストステージへ行けるでしょう。 人称代名詞と熟語 変更前: わたくしのドラゴンはサラマンダーよりも迅速に戦いの庭を駆け抜ける! 変更後: わたくしの龍はサラマンダーよりも急に戦の庭を駆け抜ける! 意味辞典はどうも人称代名詞が得意でないのか、『わたくし』を変換してくれないようです。 それと意味辞典では『戦いの庭』と『戦場』が同じ概念なのですが、優秀なJanomeは『戦いの庭』を『戦い』『の』『庭』に分割します。熟語は特殊処理してあげましょう。 冒頭の例 変更前: フォルダ1のサブフォルダとしてディレクトリ2とフォルダー3、folder4があります。 変更後: フォルダiのサブフォルダとして名鑑2とフォルダ3、小冊4があります。 うーん、ダメダメ! 一応『フォルダー』は『フォルダ』になってますけど、半角カナは対象外とか1がiになっちゃうとかそれ以前の問題ですね。 ちなみに「ディレクトリAの中身を親ディレクトリに移動する」を変換してみたら「名鑑aの刃を親名鑑に動為す」ってなりました。キメツの中身に変換されちゃいましたね。ハハハ(刃だけに)。 にゃんにゃん(形態素解析) 変更前: わがはいはにゃんにゃんではありませんにゃ。ネームはいまだに無いんですにょ。名前はナッシングって言い換えることもできるにゃね。 変更後: わがはいはにゃんにゃんではありませんにゃ。名は猶無いんですにょ。名は零って言替える事も可能にゃね。 これは話し言葉をJanomeが形態素解析できてませんでした。 トークンを出力すると下のようになります。 わがはい 名詞,代名詞,一般,*,*,*,わがはい,ワガハイ,ワガハイ は 助詞,係助詞,*,*,*,*,は,ハ,ワ に 助詞,格助詞,一般,*,*,*,に,ニ,ニ ゃんにゃんではありませんにゃ 名詞,一般,*,*,*,*,ゃんにゃんではありませんにゃ,*,* 。 記号,句点,*,*,*,*,。,。,。 『にゃんにゃん』が『に』『ゃんにゃん』になってるので猫じゃないです。名前がないどころではなく、生物かどうかも怪しいです。 考察まとめ ということでこの記事を改善してビッグ・テックだかドッグタグだかの就職に使いたい人向けに改善点をまとめます。 人称および助詞や助動詞も変換できるよう辞書を改善すべし 大文字、漢字の処理や活用形は泥臭く対応する関数を作るべし 話し言葉に合わせて形態素解析を改修すべし 誤字脱字や表記揺らぎに対応するためレーベンシュタイン距離が1の表現の訂正機能を追加すべし 転変流転する固有名詞や流行語に対応すべくスクレイピングと機械学習を取り入れるべし さらに1つの文に限らず文章全体の構成を見直して模範的な構文と序論・本論・結論の流れに書きなおす機能を追加すべし 長文を3行以内に要約する機能を高い精度で実装すべし これらの機能を全部実装して自然な言い回しへの変換や校正ができるツールを作れば一流企業もベンチャーも夢じゃないんじゃない? 責任はとれませんが。 今回のオチ オチまでの冗長な言い訳 私にとっての夢物語で記事を終えるのはポエム無情断迅拳でしかありませんので、最後に逆転の発想で話をふくらませておきます。 令和となっては必ずしも圧縮にこだわる必要はないのです。 今や潤沢な記憶領域が約束されていて、動画ならいざしらずテキストをちまちまと圧縮しても効果が見込めない時代と言えます。 じゃあなぜこのポエムを書き始めたのかはおいといて、無理に圧縮するよりも日本語の初級者が読んでも分かりやすい表現に話を膨らませた方が有用ではないでしょうか! そしてWordNetのsynset_defテーブルには単語の概念の辞書的定義が載っているのです。 例えば次のSQLを実行してみましょう。 select sd.def from word w join sense se on w.wordid = se.wordid join synset_def sd on se.synset = sd.synset where sd.lang = 'jpn' and w.lemma='猫' order by LENGTH(sd.def) desc 実行結果 def 通常、厚く柔らかい毛皮を持ち、吠えることのできないネコ科の哺乳類:家ネコ ヤマネコ 冗長なオチ ソースコードを実行してみましょう。 import sqlite3 from janome.tokenizer import Tokenizer # SQL文 sql_format = """ select sd.def from word w join sense se on w.wordid = se.wordid join synset_def sd on se.synset = sd.synset where sd.lang = 'jpn' and w.lemma='%s' order by LENGTH(sd.def) desc """ # 単語を説明に変換する def word_to_luigi(db, token): word = token.surface pos = token.part_of_speech.split(",") #print(token) if pos[0] in ["名詞", "形容詞", "副詞", "動詞"]: cur = db.execute(sql_format % word) row = cur.fetchone() return row[0] if row else word else: return word def extract_text(tokenizer, text): with sqlite3.connect("wnjpn.db") as db: return "".join([word_to_luigi(db, t) for t in tokenizer.tokenize(text)]) text_list = ["あなたは非常に美しいです。", "わたくしのドラゴンはサラマンダーよりも迅速に戦いの庭を駆け抜ける!", "フォルダ1のサブフォルダとしてディレクトリ2とフォルダー3、folder4があります。", "わがはいはにゃんにゃんではありませんにゃ。ネームはいまだに無いんですにょ。名前はナッシングって言い換えることもできるにゃね。", "ディレクトリAの内容を親ディレクトリに移動する", ] tokenizer = Tokenizer() for text in text_list: compressed = extract_text(tokenizer, text) print(f"変更前: {text}") print(f"変更後: {compressed}") print("") こうなります。 変更前: あなたは非常に美しいです。 変更後: あなたは突然の思いがけない危機(通例危険を伴う)で緊急な行動をとる必要があることに出演においてセンセーショナルであるか影響においてスリリングなです。 変更前: わたくしのドラゴンはサラマンダーよりも迅速に戦いの庭を駆け抜ける! 変更後: わたくしの通常、火を噴き、爬虫類のような体で、時として翼を持つとされるはサラマンダーよりも準備ができて、望んであるいはすぐに行動するさまに2人以上の人またはチームが競争する正式なコンテストの家や他の建物の回りの土地を駆け抜ける! 変更前: フォルダ1のサブフォルダとしてディレクトリ2とフォルダー3、folder4があります。 変更後: 中身を保護するため折り重ねられる覆いこの数を表わす最も小さな整数あるいは数字のサブフォルダとしてメモリー(普通ハードディスク)に保存されているファイルのリスト1と1の合計である基数、または、この数を表す数字と中身を保護するため折り重ねられる覆い1と1と1の合計である基数、中身を保護するため折り重ねられる覆い3と1の合計である基数があります。 変更前: わがはいはにゃんにゃんではありませんにゃ。ネームはいまだに無いんですにょ。名前はナッシングって言い換えることもできるにゃね。 変更後: わがはいはにゃんにゃんではありませんにゃ。ある人または物が認識される、言語の最小構成単位は変化、中断または停止なしで所有者を持たないさまんですにょ。誰かまたは何かが他から呼ばれる、分類されるあるいは区別されたにより、語を識別することは重要でない量って異なった言葉で同じメッセージを表現するそれ自身の存在を持つと考えられる特質または品質のいずれかも力量か能力を持つさまにゃね。 変更前: ディレクトリAの内容を親ディレクトリに移動する 変更後: メモリー(普通ハードディスク)に保存されているファイルのリストローマ字の最初の文字の感じ取ったこと、発見したこと、あるいは学習したことの全体あるいは範囲を子どもを生み出す、出産する、子供を養育して、あるいは育てる人メモリー(普通ハードディスク)に保存されているファイルのリストに新しい位置または場所に動かす、あるいは移動させる、具体的および抽象的な意味でも考慮、判断または使用のために提案する なんて平易で完璧な説明を加えた、日本語に慣れていない初学者も上級者も理解に苦しむ文章になったのでしょう! お酒に酔いながらこのプログラムを実行して結果を読んだら吹き出しました。 第二第三の犠牲者が出ませんようにお祈りしたところで、机を拭くために筆を擱かせていただきます。やばい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PythonのSympyを使って中国剰余定理(CRT)を解く

中国剰余定理 中国の剰余定理はこのWikiにもあるように以下のような定理のことです。 $k$個の整数$m_1,m_2,...,m_k$が互いに素ならば、任意に与えられる整数$a_1,a_2,...,a_k$に対して $x\equiv a_1\ (mod\ m_1)$ $x\equiv a_2\ (mod\ m_2)$ ... $x\equiv a_k\ (mod\ m_k)$ を満たす整数 x が $m_1*m_2*...*m_k$ を法として一意的に存在する。 例題 1. 7で割った余りが5かつ、11で割った余りが2となる整数を求めよ。 これは以下の式で$x$を求めることになります。 $\large x\equiv 5\ (mod\ 7)$ $\large x\equiv 2\ (mod\ 11)$ これをsympyのcrtで解くときは、$m_k$と$a_k$のリストをそれぞれ作って渡します。 import sympy.ntheory.modular m = [7,11] a = [5,2] (x, y) = sympy.ntheory.modular.crt(m,a) print(f"x = {x}, y = {y}, x%7 = {x%7}, x%11 ={x%11}") ## x = 68, y = 77, x%7 = 5, x%11 =2 $\large x=68$が$7*11=77$を法とする解となります。検算でも正しい余りになってます。 例題2. $n=77$のとき以下の式を満たす$x$をすべて求めよ。ただし$0<x<n$とする。 $\large x^3\equiv 1 \ (mod\ n)$ 中国剰余定理を逆に考えると、$n=77=7*11$なので、素数$7$と$11$の二つの式に分解できます。 $\large x_1^3\equiv 1\ (mod\ 7)\ \ ...(1)$ $\large x_2^3\equiv 1\ (mod\ 11)\ \ ...(2)$ $\large x = x_1x_2$ 各々を以下のプログラムで求めると pl = [7,11] fmod = [[i for i in range(1,p) if (i**3) % p == 1] for p in pl] print(fmod) # [[1, 2, 4], [1]] mod 7の答えは[1, 2, 4]、mod 11は[1]となるので以下の3つのケースで中国剰余定理を計算すればよいことになります。これらをsympyのcrtに渡すと答えは(1, 23, 67)となります。 (m1,a1) (m2,a2) (7,1) (11,1) (7,2) (11,1) (7,4) (11,1) import itertools for fmp in itertools.product(*fmod): (x,y) = sympy.ntheory.modular.crt(pl, fmp) print(x) # 1 # 23 # 67
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

機械学習初学者が機械学習本を輪読会で勉強したいなら

概要 最近機械学習の輪読会を開く機会があったため、今回以下の内容をまとめることにしました。 機械学習初学者の勉強方法 おすすめ輪読本 おすすめ輪読方法 ですので、 機械学習の勉強を輪読会で行いたい 機会学習初学者である という方に向けた内容となっております。 私自身は記事を書いた時点で機械学習エンジニア2年目で、機械学習の勉強自体は4年目くらいになります。 ですので私もまだまだある意味で初学者で、こういった記事を書くのは恐縮なのですが、もし「この本がいいよ!」とか「こういったやり方はどう?」など何かご意見ありましたらお気軽にコメントいただけたら嬉しいです。 機械学習の始め方 それではまず機械学習初学者の勉強方法についてですが、実はこちらに以前ロードマップをまとめています。 https://note.com/kotabrog/n/n6ca9ff6f52f2 おすすめ輪読本と輪読方法 ということで、さっそく輪読会についての話に入りたいと思います。 輪読会とは? そもそも輪読会には以下のような意味があります。 人々が集まって、同じ教科書などの本を読み、その内容について意見を交わすことを意味する語 https://www.weblio.jp/content/%E8%BC%AA%E8%AA%AD%E4%BC%9A 輪読会の良いところはいろいろあると思いますが、みんなで行うことでモチベーションを保てたり、自分にない意見を聞いてより理解を深めることができると思います。 また定期的に行ったり、発表者を担当でまわしていくようなタイプのものであれば、勉強の習慣づけにも役立てることができます。 もちろん良いことばかりではなく、自分のペースで勉強できない場合があったり、参加者のモチベーションが下がってくるとうまく機能しなくなったりもすると思います。 今回お話しする輪読会は、そんな輪読会の中でも以下のようなものを想定しています。 輪読する対象は機会学習系の書籍 参加者は全員機械学習初学者 定期的に行う 参加者は全員同じレベルくらいの方が、わからないことがあったときにみんなで解決する雰囲気になっていいかなと思うので、先生役みたいなある程度知っている人がいる場合はあまり口出ししない方がいいのかなと思っています。 また、1週間に一度など定期的に集まって、1冊の本について理解を深めていくような輪読会を想定しています。 紹介する輪読本の種類 機械学習の勉強をしたいと思った動機や興味は様々だと思います。 また理系大学出身だった人や、微分積分なんてわからんという人ももちろんいるかと思います。 ですので、機械学習初学者と一口に言ってもそういった背景がばらばらだと思うので、分野としては結構広めにご紹介したいと思います。 具体的には以下の5種類になります。 機械学習の理論の基礎系 高校数学の勉強系 大学数学の基礎の勉強系 統計系 機械学習のプログラミング系 これらについて、それぞれ自分の知っている中から何冊かを紹介していきたいと思います。 機械学習の理論の基礎系 機械学習が何をしているかの概要を知るだけでしたら、そこまで数式に頼らずとも理解することはできますが、きちんと理論を勉強するのであれば数学の知識は欠かせません。 ですので、本当に初めて機会学習について勉強する方であれば、まずはプログラミングなどで動かして体感する方がおもしろいと思います。 ちなみに1冊目に紹介する本は、機械学習に必要な数学の知識から勉強でき、しかも一応中学数学を理解していれば読むことができるようになっているので、初めての機会学習系の書籍として選んでもおもしろいかもしれません。 人工知能プログラミングのための数学がわかる本 書籍リンク まず1冊目はこちらになります。 上で少し述べているように、この本は、機械学習に必要な部分だけをかいつまんで、しかも一応中学数学を理解していれば読むことができるように作られています。 また、その数学の知識が、機械学習のどんなところで活用されているかなども書かれていて、最終的にはニューラルネットワークの仕組みを理解することができるようになっています。 半分以上が数学(微分、行列、統計)についての内容ですが、理論につながる部分があったので「機械学習の理論の基礎系」の1冊としてご紹介することにしました。 この本を読むのにおすすめな方は以下のような方になります。 高校数学があやしかったり、大学数学にふれたことがない方 とりあえずさらっと、機械学習で数学がどんなふうに使われているかを知りたい方 はじめてのパターン認識 書籍リンク 次の本は、大学数学の基礎は勉強済みという方向けになります。 おそらく、本当に機械学習が初めて、という方よりは、多少は機械学習について知っている方が読みやすいかと思います。 この本は機械学習の理論の基本的な部分について、簡潔にまとめられている本になります。 結構行間がある……つまり計算過程が省かれている部分があるので、ある意味輪読するにはぴったりかもしれません。 ちなみにパターン認識という分野についてですが、機械学習との関係としては 機械学習の一部と言われることもあるが、機械学習が計算機科学の見地から生じたものであるのに対し、パターン認識は工学を起源とする。 https://kakeruai.jp/glossary/pattern-recognition/ ということだそうです。 他にも有名なものとして「パターン認識と機械学習」という書籍がありますが、こちらもパターン認識という語が入っていますね。 ということでまとめると、はじめてのパターン認識を読むのにおすすめな方は以下のような方になります。 大学数学の基礎は勉強済み 多少は機械学習について知っている ある程度しっかりと機械学習の理論の基礎を勉強したい その他 多少機会学習について知っていて、もう少し機械学習でやりたいことが定まっている方に関しては、例えば画像認識について特化した理論書や、深層学習などといった手法に関して特化した理論書をやるのもおもしろいと思います。 そういった分野ごとに特化した内容を扱っているシリーズとして、機械学習プロフェッショナルシリーズというシリーズがあるのですが、以前そのシリーズについてまとめた記事を書いたので、もしご興味ある方はこちらもどうぞ。 https://note.com/kotabrog/n/n23bf928735cb?magazine_key=m951ae49a3fc9 高校数学の勉強系 機械学習の理論をしっかりと理解するためには大学数学の知識が必要ですが、それどころか高校数学もかなり怪しい……という方ももちろんいるかと思います。 そんな方は、一度高校数学をしっかりと勉強するのもいいかもしれません。 ということで高校数学の書籍を紹介したいのですが、あまり高校数学の勉強を最近やっていないので、良い輪読本候補があまり思いつきませんでした……。 ですので、あくまで参考程度にご覧ください。 チャート式 書籍リンク 私が高校生のときは、このチャート式で数学を勉強していました。 このシリーズは例題や演習問題が豊富で、一度教科書で勉強した内容を定着させるために、とにかく問題を解きまくるための本、という立ち位置なのかなと思っています。 そんな演習用の本を輪読?と思うかもしれませんが、ちょっと特殊な形式ですがやろうと思えばできると思います。 方法について詳しくはこの記事の最後で紹介したいと思います。 ちなみに難易度としては白<黄<青<赤なのですが、これを読む方はおそらく受験生ではないので、黄色くらいでいいのではないかと思います。 (リンク先のものが黄色です) この本がおすすめの方は、以下にあてはまる方になります。 教科書を読むだけでなく、手を動かしたい 数Bや数Ⅲなど、触れたことがないものがある 機械学習の理論についての本を読もうとしたら、数式の部分で全然歯が立たなかった 新体系 高校数学の教科書 書籍リンク こちらは上下巻で3年間の数学が学べる本になっています。 普通の教科書とは違って、数学1、2、3、A、Bのように分かれているのではなく、例えば方程式についてや微分についてなど、分野で内容が分かれているので、大人になってから読むにはぴったしだと思います。 ただ、少し内容が難しいので、数学が苦手な方だと苦戦するかもしれません。 この本がおすすめの方は、以下にあてはまる方になります。 高校のときの体系ではなく、できるだけコンパクトに学びたい 数学がそんなに苦手ではない その他 輪読向きではないかもしれませんが、単純に数学の勉強をするのにおすすめするのであれば、数学ガールの秘密ノートシリーズはとてもおすすめです。 数学ガールの秘密ノート/式とグラフ もともと数学ガールは数学を題材にした小説(?)で、この秘密ノートシリーズは番外編として高校数学くらいの内容を学ぶことに特化したものになっています。 通常の数学ガールと比べて物語は少ないですが、本当にわかりやすく、そして楽しく学ぶことができるので、とてもおすすめです。 大学数学の基礎の勉強系 機械学習の理論を理解するのに必要な大学数学の基礎としては、統計学を除くと大きく分けて以下の2つになります。 微積分 線形代数 もちろん、機会学習の中でも特定の分野だったりではまた別の数学が必要になってきますが、基本的な部分を抑えるにはこれらをある程度知っていれば大丈夫だと思います。 とはいっても、例えば理系大学の1,2年目で習うすべての微積分と線形代数が必要というわけではなく、機会学習に必要な部分は限られています。 ですので、一般的な微積分や線形代数の本を読むのは若干必要以上に学ぶことにもなるかもしれませんが、概観を知ればより理解は深まりますし、無駄にはならないと思います。 ちなみに、このレベルの微積分と線形代数は、少し高度な統計学の理論を理解するために一部必要になる部分があります。 もちろんこれもどこまで深く理解したいか……によりますが。 また大学数学の教科書もそこまで詳しいわけではないので、こちらも参考程度にご覧ください。 (ここにあげるレベルくらいのものであればなんでもいいと思います) スッキリわかる線形代数 書籍リンク こちらは線形代数の本になります。 線形代数で機械学習にもっとも関係する分野は行列になります。 機械学習の入力としては様々なデータが入れられますが、それらはベクトルや行列としてあらわされることが多いです。 例えば猫の画像が与えられたとしたら、その猫の画像は、画素一つ一つが要素となっている行列としてあらわされたりします。 また、たくさんのデータを扱う場合、並列に計算ができる行列はとても便利なので、機械学習の理論を勉強するうえで行列の計算は必要不可欠なのです。 この本がおすすめの方は、以下にあてはまる方になります。 線形代数についてしっかりと学びたい 微分積分学 (数学シリーズ) 書籍リンク 次は微積分の本になります。 高校数学で微積分はやりますが、多変量の微積分が機械学習では必要になるため、大学数学としての微積分もある程度必要になります。 特に機械学習で必要なのは微分になります。 機械学習は、学習をする過程で、ペナルティが小さくなるようにしていくのですが、どこをどう変化させればどれだけペナルティが減るのか……というのを考えるために、変化の度合いを表す微分が使われます。 また、統計をしっかりと学ぶためには、確率が積分であらわされるので、ある程度積分の知識も必要になります。 この本がおすすめの方は、以下にあてはまる方になります。 微積分についてしっかりと学びたい 統計系 機械学習と統計は切っても切れない関係にあります。 例えば、機械学習で得た結果があっても、それがどれくらい当たっているのかをきちんと示すには確率を使う必要があります。 また、データがどういった傾向にあるのかなどを知っておかないと、とんちんかんな方向に機械学習を使ってしまうこともあるかもしれません。 ですので、ある程度の統計の知識は必要不可欠といってもいいくらいに必要だと私は思っています。 また、機械学習の理論的な部分について知りたい場合には、その背景に統計学的な要素が含まれることもあります。 ですので、もし理論の勉強をしたい場合には、統計についてより知っておいた方が、理解が深まるかもしれません。 統計Web リンク 1冊目……というか、本ではないのですが、まず最初におすすめするのはこちらの統計Webになります。 こちらはなんと無料で統計検定2級相当の内容を学ぶことができるようになっていて、とりあえず統計を学びたい方すべてにおすすめしたいくらいの内容になっています。 統計検定2級は大学で学ぶ統計学の基礎くらいのレベルなのですが、簡単なところから丁寧に解説されていて、なんと中学数学がわかれば読めるように一応作られています。 輪読としてやるのであれば、これをきっかけにみんなで統計検定2級を実際にとるのもいいかと思います。 その際は、この統計Webを勉強した後に、公式の過去問を読めば、おそらく合格までもっていくこともできるかと思います。 ということでこのサイトがおすすめの方は、以下にあてはまる方になります。 統計学を勉強したい 統計検定をとりたい 統計学入門 (基礎統計学Ⅰ) 書籍リンク 正直初学者であれば統計Webを全員におすすめしたいところなのですが、それでは生ぬるい!もっと統計を勉強したい!という方は、こちらの本がおすすめです。 統計Webでは紹介されているだけの理論の証明を行っていたりするので、もし機械学習の理論をしっかりと勉強したい場合には、一度こちらを勉強しておくと理解がより深まるかもしれません。 この本がおすすめの方は、以下にあてはまる方になります。 統計Webでは物足りない 証明含めしっかりと統計の勉強をしたい 機械学習のプログラミング系 機械学習初学者であれば、まずは動いているところを見た方がイメージもつきやすいしテンションも上がるのかなと思います。 ですので、基本的にはプログラミングでまずは動かす……というところを初学者にはおすすめしたいのですが、初学者向けのプログラミング本は理解というよりは演習っぽい側面の方が強いと思うので、みんなで理解を深める輪読とはあまり相性がよくないとも思います。 ですが、しっかりと理論的な部分も含まれている書籍や、輪読のやり方によっては、もちろんプログラミング系の本も実のある輪読会にできると思うので、こちらで紹介していきたいと思います。 ちなみに今回紹介するものはすべてプログラミング言語がPythonになっています。 Pythonの基礎についてはそこまで深く事前に勉強しなくても、都度必要になったことを学べばそれで十分だと思います。 まったくプログラミングをしたことがない方は、最初にあげたロードマップにのっている方法で、少し触れてみることをおすすめします。 PythonとKerasによるディープラーニング 書籍リンク こちらはkerasという機械学習フレームワークを用いて、様々なディープラーニングを体験しようという内容になっています。 理論的な部分は少なめですが、とにかくディープラーニングを簡単に体験でき、かつ実践的な内容まで載っているのでとてもおすすめです。 この本がおすすめの方は、以下にあてはまる方になります。 実践的な内容を学びたい できるだけ手軽にある程度高度な機械学習を体験したい 第3版 Python機械学習プログラミング 達人データサイエンティストによる理論と実践 impress top gearシリーズ 書籍リンク 次に紹介するのは基本的な理論もある程度載っている本になります。 その代わり、「PythonとKerasによるディープラーニング 」より紹介している内容が基礎的な部分になっています。 基礎的だからといって簡単なわけではなく、むしろディープラーニング以外の古典的な手法の理論も含めて、段階的に学ぶことができます。 またそのため、約700ページもあるので、なかなか覚悟して挑まないといけないかもしれません……。 この本がおすすめの方は、以下にあてはまる方になります。 理論もある程度学びたい 機械学習について、段階的に学びたい ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装 書籍リンク 機械学習は普通フレームワークやライブラリといった、簡単に機械学習を実装できる仕組みを使って実装するのですが、この本はその仕組みは使わずに、自分で機械学習を作ろうという内容になっています。 自分で作るので、もちろんある程度理論的な部分も理解することができます。 また、Pythonについての簡単な説明も最初に載っています。 実は私の最初の機会学習本はこの本で、そのおかげでこのあたりの内容については今でもよく覚えています。 この本がおすすめの方は、以下にあてはまる方になります。 ゼロから機械学習のアルゴリズムを作ってみたい 機械学習が行われているときにプログラム的に何が起こっているかも理解したい 輪読の方法 ということで、書籍の紹介は以上になります。 ここからは、いくつかの輪読会の方法のアイデアについてお話していきます。 担当者を決めて決まった範囲を発表する 担当者を決めて発表するが、ページを決めずに、みんなが理解できたところまで進める 問題の作成者を決めて、それをみんなで解く。 理論とプログラムの時間をわけて担当者が発表する 司会だけ決めて、目標が達成できたかや、わからないところを聞く時間にする 担当者を決めて決まった範囲を発表する これが一番一般的なやり方だと私は思っています。 例えば交代で担当者を割り振って、その週の担当者が発表をして、発表途中や発表後にどんどん発表者に質問をして、みんなで理解を深めていく……というようなイメージです。 次の項目との違いである範囲を決めることには以下のメリットがあります。 担当者以外が輪読会に参加する準備がしやすい 輪読会自体がいつ終わるかが明確になりやすい 発表時間が限られているので大事なポイントにしぼって学びやすい 小さい部分にとらわれず、全体をつかみやすい あまり長い期間輪読会をしているとモチベーションが保てなくなることもあると思います。 発表範囲を明確にしてゴールが明確になるので、モチベーションは保ちやすいでしょう。 また、細かい部分にとらわれすぎると結局全体として何をやったのかを見失ってしまう……ということもあると思うので、1つの範囲に対して発表時間が決まっていればその点はある程度大丈夫かと思います。 ただ、逆に言えば細かい部分まですべて発表することはできないので、ふわっと発表して終わってしまうことがないように注意する必要があります。 そのあたりは、担当者以外も輪読会の前に勉強しておいて、わからなかったところを輪読会で発表者に質問するなどすれば、ある程度補えるかもしれません。 担当者を決めて発表するが、ページを決めずに、みんなが理解できたところまで進める 次の例は1つ前と違って範囲を決めずに行うパターンです。 こちらには上の方法と比べて以下のメリットがあると思います。 細かい部分含めてしっかりと理解ができる 担当者以外はどこまで発表が進むかわからないので、予習をある程度先まで行える 範囲を決めて行う場合と違って、細かい部分までじっくり学ぶことができるのが一番のメリットだと思います。 しかし、間延びしやすいという欠点があるので、発表時はその点を気を付けなければなりません。 例えばみんながわかっている部分は飛ばしたりするとテンポよく進めることができるでしょう。 また、モチベーションを保つのも、おそらく範囲を決めるよりは難しいと思います。 最初のうちはいいかもれませんが、時間がたつと「もしかしたら次回はそんなに進まないかも……」と思って発表者以外が全然予習をしなくなってしまうこともあるかもしれません。 そういったことを避けるためにも、ある程度目標を設けたり、発表の前にある程度発表範囲を連絡しておくなどといったことをするといいでしょう。 問題の作成者を決めて、それをみんなで解く これはもはや輪読といっていいのか微妙なところですが、例えば演習問題が多い書籍で行うとおもしろいかもしれません。 例えば、高校数学系の書籍紹介であげた本をやるときとかですね。 具体的には、まず問題作成者を毎週交代で決めて、その人が問題を作成してきます。 問題は、事前に決めた本の範囲の中から作成者の好みで作成します。 そして輪読会を行う時間に、参加者みんなに解いてもらいます。 解き終わったら、解けた人のうちの誰かか作成者が、解説をする……といったような流れです。 このようにすれば、作成者は問題を作れるくらいには理解していないとですし、解答者もどの問題が出るかわからないのできちんと理解ができるまで勉強する必要があります。 この方法なら、例えば資格の勉強をしたりといったテスト勉強としても有効だと思います。 理論とプログラムの時間をわけて担当者が発表する 機械学習本は理論とプログラムの両方について書かれている書籍もたくさんあります。 ですので、例えば前半は理論部分を、後半はその理論に基づいたプログラムを実際に見せる、というような形式でも発表することができると思います。 また、このやり方は理論系の内容しか書かれていない本でも実践することができます。 その際は難易度が少し上がってしまうので、参加者がある程度プログラミングができることが前提となりますが……。 司会だけ決めて、目標が達成できたかや、わからないところを聞く時間にする 最後の方法は、発表を行わない形式です。 輪読会時は進捗報告やわからなかったところをみんなで考える時間にします。 この方法は例えば発表には向かないタイプの本を輪読するのにいいかもしれません。 例えば演習系の本や、プログラミング系の本はこの方法でもいいかと思います。 また統計検定などの資格取得を目指すのにもいいかもしれません。 ただ、この方法の注意点としては、やはりモチベーションを保つことにあると思います。 他の輪読法だと、自分が発表者のときにさぼってしまうと周りに迷惑をかけてしまうので、やらざるを得ない状態を作ることができますが、この方法だと誰の迷惑にもならないのでついついさぼってしまうということが起きやすいと思います。 輪読会の注意点 ということで最後になります。 これで書籍と輪読の方法の紹介は終わりましたが、細かい注意点などを最後にのせておきます。 発表形式 発表方法は私はなんでもいいと思っています。 例えばパワポを使ったり、ブログにしたり、電子ノートを画面に映したり、ホワイトボードを使ったり……と様々だと思います。 ですが、例えば初回の人はみんながどんな発表を想像しているのかがわからないでしょうし、輪読会の最初で軽く認識合わせをするのがいいかもしれません。 輪読会以外での質問 せっかく同じ本を勉強する仲間がいるのですから、輪読会時以外でも積極的にわからないところは質問できるといいと思います。 ですので、まず輪読会を行う前に、チャット形式で連絡がとれる手段を用意すると、スムーズにいくのではないかなと思います。 参加者の背景の差 輪読会の参加者にはいろいろな背景がある場合もあると思います。 ですので、その差が大きい場合には、わかる人にとっては退屈なものになったり、わからない人にとっては結局最後までわからないということが起きてしまうかもしれません。 そういったことがないように、わからない場合は質問をする、わかる場合はそれに答える、というやりとりを積極的に行って、みんながより理解を深められるように意識することが大切だと思います。 リアクションを! 発表に慣れていない人もいると思いますし、聞いているときはできるだけ優しいリアクションをしてあげましょう。 リアクションがないと、なんだか自分一人でただただ喋っているだけ……という気分になることもあると思います。 また、リアクションの一つとしての質問も、会を盛り上げるためには重要な要素です。 そしてもちろん、発表者も余裕があればそんなリアクションを取りやすいように、例えば誰かに話題を振ったりなどができると、より会が盛り上がるかと思います。 区切りがよくなったら質問の時間を作るとかもいいかもしれません。 終わりに さて、結構長くなってしまいましたが、初学者への機械学習輪読会のすゝめはこれで以上になります。 参考にしていただけたらとてもうれしく思います。 また、冒頭でも述べましたが、もし「こんな本もおすすめだよ!」などいったことがありましたら、ぜひコメントお願いします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Django】ユーザーモデルAbstractBaseUserの実装(Twitterっぽく)

ユーザーモデルの種類 User(標準モデル) AbstractUser AbstractBaseUser(オススメ) 各モデルの違い 標準モデル(難易度低)  簡単だが応用が効かない。 models.py from django.contrib.auth.models import User AbstractUser(難易度中)  フィールドのカスタマイズ(追加・変更)ができる。 models.py from django.contrib.auth.models import AbstractUser AbstractBaseUser(難易度高)  フィールドのカスタマイズ(追加・変更・削除)ができる。 models.py from django.contrib.auth.models import AbstractBaseUser 公式推奨  後でユーザーモデルを変更するのは、超大変なので最初からAbstractBaseUserを使うのがオススメだそうです。 チュートリアル等はUser(標準モデル)でも十分です。 初回migrate前にAbstractBaseUserを定義する プロジェクト開始前にどのタイプにするかよく考える事 デフォルトのフィールド デフォルトのフィールドを参考にする。 引数はあまり気にせず、どんなフィールドが作られるのかだけを見てもらえればOKです! この中からよく使われそうな物をピックアップして、さらに新たなフィールドを追加してみたいと思います。 ちなみに、User(標準モデル)とAbstractUserを使った場合は、以下のフィールドが作成されます。 migtarion.py fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('password', models.CharField(max_length=128, verbose_name='password')), #最終ログイン日時 ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), #スーパーユーザー真偽 ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), #ユーザーネーム ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), #ファーストネーム ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), #ラストネーム ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), #メールアドレス ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), #スタッフ権限 ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), #ログイン可不可 ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), #登録した日時 ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), #グループ ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), #パーミッション ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), ], モデルの作成 とりあえず適当に必要なフィールドを書いちゃいます。 ユーザーネーム(Twitterの@以降を想定) email ニックネーム 誕生日 プロフィール画像 血液型(choices=のサンプルとして) url 自己紹介文 登録日 ログインの可否(管理者用) models.py from django.db import models from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin #usernameでバリーデートするために入れときました(無くてもOK) from django.core.validators import MinLengthValidator, RegexValidator class MyUser(AbstractBaseUser, PermissionsMixin): BLOOD_CHOICES = [ ("A型","A型"), ("B型","B型"), ("AB型","AB型"), ("O型","O型") ] username = models.CharField(verbose_name='username', max_length=10, unique=True, validators=[MinLengthValidator(5,), RegexValidator(r'^[a-zA-Z0-9]*$',)]) email = models.EmailField(verbose_name='Email', max_length=50, unique=True) nickname = models.CharField(verbose_name='ニックネーム', max_length=10, blank=False, null=False) date_of_birth = models.DateField(verbose_name="誕生日", blank=True, null=True) image = models.ImageField(verbose_name='プロフィール画像', upload_to="image/", blank=True, null=True) blood_type = models.TextField(verbose_name="血液型", choices=BLOOD_CHOICES, blank=True, null=True) url = models.URLField(verbose_name='リンク', blank=True, null=True) introduction = models.TextField(verbose_name='自己紹介', max_length=300, blank=True, null=True) date_joined = models.DateTimeField(verbose_name='登録日', auto_now_add=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) is_admin = models.BooleanField(default=False) #AbstractBaseUserにはMyUserManagerが必要 objects = MyUserManager() #一意の識別子として使用されます USERNAME_FIELD = 'email' #ユーザーを作成するときにプロンプ​​トに表示されるフィールド名のリストです。 REQUIRED_FIELDS = ['username'] def __str__(self): return self.username is_activeについて イメージしやすいように例えるとBAN機能のようなものです。 チェックを外すとそのユーザーがログインできなくなります。 MyUserManager 管理者が管理コマンド上(ターミナル等)でユーザーを作成する時に必要です。次の項目で書きます。 USERNAME_FIELD ログインする時の一意の識別子です。 Twitterであれば、emailとかusernameになりますね! unique=Trueのフィールドのみ設定できます。 REQUIRED_FIELDS 管理者が管理コマンド上(ターミナル等)でユーザーを作成する時に表示されるリストです。 上記では'username'だけになっていますが、複数設定することも可能です。 models.py #複数設定する場合はこんな感じ REQUIRED_FIELDS = ['username', 'date_of_birth'] MyUserManagerの実装 前述の通り、管理者が管理コマンド上(ターミナル等)でユーザーを作成する時に必要なので作成したMyUser(AbstractBaseUser)の上に書いておく。 models.py from django.db import models from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin from django.core.validators import MinLengthValidator, RegexValidator class MyUserManager(BaseUserManager): def create_user(self, username, email, password=None): if not username: raise ValueError('Users must have an username') if not email: raise ValueError('Users must have an email address') user = self.model( username=username, email=self.normalize_email(email), ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, username, email, password): user = self.create_user( username=username, email=self.normalize_email(email), password=password, ) user.is_admin=True user.is_staff=True user.is_superuser=True user.save(using=self._db) return user class MyUser(AbstractBaseUser, PermissionsMixin): #省略 大体こんな感じで大丈夫! 変更するとしたらif not hoge:と同時にuser=の引数くらいでしょうか? 他はコピペで大丈夫だと思います。 デフォルトのユーザーモデルに上書き 上書きしないとエラーになるのでsetting.pyに設定を書きます。 setting.py AUTH_USER_MODEL = 'アプリ名.モデルのクラス名' #サンプル AUTH_USER_MODEL = 'accounts.MyUser' マイグレーションしてみる terminal $ python manage.py makemigrations $ python manage.py migrate スーパーユーザーの作成 スーパーユーザーはmigrate後に作る事 terminal $ python manage.py createsuperuser まとめ AbstractBaseUserはかなり応用が効くみたいで、SNS認証が必要な場合にも使えます。 サービス公開を目的とする場合は是非AbstractBaseUserを利用してみて下さい! 応用が効きすぎて色んな設定がありますので、詳しく知りたい方は公式ドキュメントを参照してくださいね! こんなの作ってます オンラインでの仲間づくりに活用できる、心理学を利用したレジュメサービスです。 自分や色んな人の性格を見ることができますよ! 無料なので使ってみてもらえると嬉しいです!(フィードバックも歓迎です!)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

昨日の記事からの続きとなります。 ◆事前準備編◆  1.macOSのデスクトップにtempフォルダを作る  2.tempフォルダにccxtをpip3でインストールする  3.既に作ったpythonプログラムを lambda_function.py へリネームしてtempフォルダに入れる  4.lambda_function.py の先頭に指定プログラムを追記する  5.ターミナルでzipでまとめる ◆Lambda編◆  6.AWSへログインし、5をアップロードする ◆EventBridge編◆  7.EventBridge (CloudWatch Events)に定期実行ルールを登録する 今回は7の説明をします。 手順 7.EventBridge (CloudWatch Events)に定期実行ルールを登録する 7-1.AWSのマネジメントコンソールヘログインします。 https://aws.amazon.com/jp/console/ 7-2.上部の検索バーに Lambda と入力します。Lambda をクリックします。 7-3.手順6で作成した関数をクリックします。 7-4.[+トリガーを追加] をクリックします。 7-5.[トリガの設定] で [ EventBridge ]と入力します。 7-6.[新規ルールの作成] を選択し、ルール名に任意の名前を入力します。(例:everyday_1H) スケジュール式にcron形式で実行タイミングを記載します。 今回は1H毎に毎日Pythonを実行して欲しいので、以下のように書きます。 cron(0 * ? * * *) 最後に[追加]をクリックすると完成です。 cronの書き方 以下に公式の記載がありますが、英語なのでよくわからんって人向けにサンプルを記載します。 Minutes 分 Hours 時間 Day of month 何日 Month 月 Day of week 曜日 Year 年 Meaning 意味 0 10 * * ? * 毎日AM10時に実行 15 12 * * ? * 毎日PM12:15に実行 0 18 ? * MON-FRI * 毎週月〜金PM6時に実行 0 8 1 * ? * 毎月1日AM8時に実行 0/15 * * * ? * 15分毎に実行 0/5 8-17 ? * MON-FRI * 毎週月〜金AM8時〜PM5時55分まで5分毎に実行 最後に いかがでしたでしょうか。定期実行されましたでしょうか。 cronの書き方はくせがあるので、上記を見て理解して頂ければと思います。 PythonをAWSへ移行するまでを3つの記事で解説しました。 今日から私のオンプレmacOSはクラウドリフトされ、本番機からPython開発機となりました。 ご参考になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python初学者の備忘録 ⑦代数演算子について

python,Qiita初心者なので、備忘録として記載していきます。 なにせ初心者なので、知識不足はご理解ください。 知識を深めながら追記していきたいと思います。 代数演算子 基本 変数に対して加減乗除の計算に使用する 以下の演算子がある 記号 式 説明 +a +a 整数 -a -a 負数 + a + b 足し算 - a - b 引き算 * a * b 掛け算 / a / b 割り算 % a % b a/bの余り ** a ** b aのb乗 // a // b 割り算(切り捨て) 演算子の優先順位 「+a」,「-a」>「**」>「*」,「/」 > 「//」>「%」>「+」,「-」
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

openSMILEをPythonで使ってみた

前書き Pythonで使える音響特徴量の解析ライブラリはないかと探してたら,openSMILEがPythonのライブラリに対応したという記事を見かけた openSMILE3.0がリリースされた(Nafuel氏による投稿) あらこれは素晴らしい,ということでNafuel氏の投稿や公式リファレンスを見ながら触ってみることにした 動作環境(Anacondaの仮想環境として構築) Python 3.7.10 opensmile 2.2.0 これは公式リファレンスに書いてある通りpip install opensmileでインストールしたった ※ちなみに,マシンはWindows10だけど特にopenSMILEのbuildとかをしたわけではない 使ってみる ファイル名を入力としてみた リファレンスのトップページに「Code example」とあるので,それを参考にした import opensmile smile = opensmile.Smile( feature_set=opensmile.FeatureSet.ComParE_2016, feature_level=opensmile.FeatureLevel.Functionals, ) feat = smile.process_file('audio.wav') print(feat.shape) # [1 rows x 6373 columns] まず,smile = opensmile.Smile(...)でインスタンス化した関数を作っているわけだが,ここでfeature_setとfeature_levelを指定する必要がある feature_set opensmile.FeatureSetを指定することで,どのような特徴量をセットするのかを決めれる ComParE_2016(デフォルト値) INTERSPEECH 2016のパラ言語チャレンジのベースラインで使われていた特徴量っぽい GeMAPS GeMAPSv01a GeMAPSv01b eGeMAPS eGeMAPSv01a eGeMAPSv01b eGeMAPSv02 emobase feature_level opensmile.FeatureLevelを指定することで,特徴量の計算レベルを決めれる LowLevelDescriptors: フレーム単位で各種特徴量を計算する,略称はLLD LowLevelDescriptors_Deltas: LLDのデルタ特徴量を計算する 現状,ComParE_2016のみに対応しているみたい Functionals: LLDの統計値を計算する(デフォルト値) featにはpandasのDataFrame形式で特徴量が格納されている 公式リファレンスやprint(feat.shape)の通り,特徴量は6373次元あるみたい 比較的特徴量次元数の少ないGeMAPSv01bやeGeMAPSv02でもそれぞれ62, 88次元あるみたい 特徴量の命名規則はココとかココを見ると良さそう 機械学習に入れるなら,PCAやLDAみたいな次元圧縮をかけるか,DNNなどに要・不要まで判断させるといいのかも LDDの結果を使うなら次元圧縮よりはDNNに判断させる方がいいかもしれない 分析に使うなら,相関係数とか有意確率に対して閾値を設けて,それでいったん重要そうな音響特徴量だけピックアップする方がいいかも LLD使ってみる import opensmile smile = opensmile.Smile( feature_set=opensmile.FeatureSet.ComParE_2016, feature_level=opensmile.FeatureLevel.LowLevelDescriptors, ) feat = smile.process_file('audio.wav') print(feat.shape) # [858 rows x 65 columns] LLDだと音響特徴量が統計値でまとめられることはないのでrawな値が65次元出てきている 音声は8.62秒のものを使ったが,それに対して858フレームの特徴量が抽出されている python上だと何をどうしても見にくいと思うので,feat.to_csv('feat.csv')とでもしてcsvファイルに書き出してあげるといいかもしれない 特徴量(feat)はpandasのDataFrameとして出力されるので,外部ファイル出力もDataFrameのそれと同じ numpy配列の信号を入力としてみた import numpy as np from scipy.io import wavfile import opensmile smile = opensmile.Smile( feature_set=opensmile.FeatureSet.ComParE_2016, feature_level=opensmile.FeatureLevel.LowLevelDescriptors, ) rate, signal = wavfile.read('audio.wav') feat = smile.process_signal(signal / 2**15, rate) print(feat.shape) # [858 rows x 65 columns] 色々と試してみたが,信号を入力とするときは-1から1の範囲に正規化されたnumpyの配列を渡す必要がある openSMILEはその前提で動いているようなので,それ以外(例えばnp.int16型)だとおかしな結果になる scipy.io.wavfile.read()はwaveファイルのヘッダ情報を見て変数の型を決めているようで,ここではnp.int16で読み込まれているため2の15乗で正規化している ちなみに,他の読み込み方だと… librosaは正規化された信号を取得できるが,入力にサンプリング周波数を渡してやる必要があるのでちょっと使いにくいかも 標準ライブラリのwaveを使う場合や,rawファイルをopen()で読み込む場合だとbytes型で読み込まれるので,np.frombuffer()などでnumpyの配列に直す必要がある VAD前と後でなんか違う? import numpy as np from scipy.io import wavfile import webrtcvad import opensmile def VAD(signal, rate, mode=0, frame_duration=10): vad = webrtcvad.Vad(mode) idx = 0 length = int(rate / 1000 * frame_duration) out = [] while idx<signal.shape[0]: if (signal.shape[0]-idx)<length: frame = signal[-length: ] else: frame = signal[idx: idx+length] try: if vad.is_speech(frame.tobytes(), rate): out.append(signal[idx: idx+length]) except: set_trace() idx += length return np.concatenate(out).astype(np.int16) smile = opensmile.Smile( feature_set=opensmile.FeatureSet.ComParE_2016, feature_level=opensmile.FeatureLevel.Functionals, ) rate, signal = wavfile.read('audio.wav') # print(signal.shape[0] / rate) -> 8.6210625 (sec.) vad_signal = VAD(signal) # print(vad_signal.shape[0] / rate) -> 6.99 (sec.) feat = smile.process_signal(signal / 2**15, rate) vad_feat = smile.process_signal(vad_signal / 2 **15, rate) print(feat["F0final_sma_amean"].values) # [166.37993] print(vad_feat["F0final_sma_amean"].values) # [166.49992] webrtcvadで10ミリ秒ごとにVADをかけてみた VADの結果,おおよそ1.63秒ほどが非音声区間として落とされている VAD前後で値が異なることから,何かしらの影響はある模様 一方で,LLDの結果で単純にF0を平均してみると104.1329となることから,おそらく音声確率あたりを使って内部ではVADらしい挙動が走っているとみなしていいと思う 数値の違いはwebrtcvadとopensmileの音声判定基準の違い,opensmileのアルゴリズムの問題あたりだと思う opensmileの特徴量にはsmaという名前がついているが,これは公式リファレンス曰く「they were smoothed by a moving average filter with window length 3.」とのことである. window length 3は多分,3フレームの窓ということだろう…(そう信じたい,少なくとも3秒はおかしい) 仮に音声確率などに関係なくsmoothingしているとすると,その影響もあるかもしれない まとめ openSMILEをPythonで触ってみた ソースからのbuildなど必要なく,pipだけで簡単に導入できる openSMILEに実装されている多種多様な音響特徴量を手軽に調べることができる 入力も音声ファイルそのもの・numpy配列のどっちでもいける ただしnumpy配列を使う時は,信号強度を-1から1の範囲に正規化しないといけない フレーム単位での特徴抽出もできるし,音声ファイル全体で統計的に丸めることもできる 統計的に丸める場合は,VADのようなことを内部でやってくれているみたい 今回は1つのファイルを処理しただけだけど,opensmile.Smile.process_files()とか複数ファイルをlist型で与えてどんどん処理するとかもいけそう
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

よく使う

githubで個別ファイルのダウンロード 個別ファイルのURLをDownGitへコピペ https://downgit.github.io/#/home OpenCV 顔検出Haar-cascade検出器の読み込み OpenCVライブらいから読み込み cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_alt2.xml') ローカル環境から読み込み 検出器をダウンロードし、ローカル環境へ保存 cascade = cv2.CascadeClassifier('ローカル環境のパス/haarcascades/haarcascade_frontalface_alt2.xml') Pythonクラスの情報確認 dir(class) class__doc__ class.__dict__
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

kivy-iosのインストールでtoolchain build kivy python が途中までコンパイルできるのにエラーが出る時

URLリンク切れかも その場合、以下のディレクトリのファイルをいじる。 "作業ディレクトリ/lib/python3.x/site-packages/kivy-ios/recipes/freetype/__init__.py"の8行目URLを "http://download.savannah.gnu.org/releases/freetype/freetype-{version}.tar.bz2" から "http://download.savannah.gnu.org/releases/freetype/freetype-old/freetype-{version}.tar.bz2" に変更すればコンパイルできる。 アーキテクチャがarm64かも(m1 mac) armになっていると、コンパイルが通らない。 homebrewのパッケージインストールからx86_64に切り替えてやり直す。(環境をどちらか一方に固定する場合) armとx86_64環境を切り替えて使用したい場合は、下記リンクのようにするといいかもしれない。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

問題解決能力を鍛える! アルゴリズムとデータ構造 Pythonで書きました(Code5.3)

Topic pythonで動的計画法の緩和のchminを実装する. chminの実装方法 C++で実装すると... template<class T> void chmin(T& a, T b){ if (a > b){ a = b; } } これを自力で,pythonに直してみる. def chmin(a,b): if a > b : a = b うまくいかない. 多分,参照型がうまくいってないのかもしれない. (原因がわからないので,わかる方はコメントお願いします) 色々漁ってたどり着いた方法がこちら↓ def chmin(dp,i,x): if dp[i] > x: dp[i] = x つまり,dpも渡してしまおう作戦.dp渡してiで配列のインデックスを指定する感じ. 問題解決能力を鍛える! アルゴリズムとデータ構造のCode5.3をpythonで書き換えるとこんな感じ. def chmin(dp,i,x): if dp[i] > x: dp[i] = x n = int(input()) h = list(map(int,input().split())) dp = [float("inf")]*len(h) dp[0] = 0 for i in range(1,len(h)): chmin(dp,i, dp[i-1]+abs(h[i]-h[i-1])) if i > 1: chmin(dp,i,dp[i-2]+abs(h[i]-h[i-2])) print(dp) では今日はこの辺で. 読んでいただいてありがとうございました?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python三次元ポアソン方程式による静電場解析 (3)一様に帯電した球の電場解析

1. はじめに  Python三次元ポアソン方程式による静電場解析により,基本的課題として空間上の点電荷の電場解析が可能なことを先の投稿において報告した(1).  本稿では,応用課題として一様に帯電した球を対象に下記を実施する. (1)三次元ポアソン方程式を差分化(再掲) (2)繰り返し法によるプログラムを作成. ①メッシュ幅は1.0と0.5を選択できるようにする. ②電位を計算 ③得られた電位から電場を計算 (3)計算結果を理論値と比較することにより解析の妥当性を検証 2. 例題の問題設定 各軸のメッシュ数は50とし,中心に一様に帯電した球を配置する.       図1 問題設定-一様に帯電した球の配置 3. ポアソン方程式の離散化(再掲)  下記に手順を記す.  ここで注意が必要な点はρは電荷密度であることである. 4. プログラム import pprint from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import numpy as np import matplotlib.cm as cm delta_L=0.5 #delta_L=1.0 LN=50 HLN=int(LN/2) nx = LN ny = LN nz = LN xmin = 0 xmax = LN*delta_L ymin = 0 ymax = LN*delta_L zmin = 0 zmax = LN*delta_L p = np.zeros((nz, ny, nx)) pd = np.zeros((nz, ny, nx)) charge = np.zeros((nz, ny, nx)) x = np.linspace(xmin, xmax, nx) y = np.linspace(ymin, ymax, ny) z = np.linspace(zmin, zmax, nz) eps0=1 y00=1 Q1=1/delta_L**3 for i in range(0, LN): for j in range(0,LN): for k in range(0,LN): rr=np.sqrt((i-HLN)**2+(j-HLN)**2+(k-HLN)**2) if rr <= 3.5 : charge[i,j,k] = Q1 elif rr <= 4.5: charge[i,j,k] = Q1*(4.5-rr) # print("Riron") for i in range(1,5): r=i*delta_L x2=-Q1*r**2/6+Q1*(4*delta_L)**2/2 print('z=',r,' V=',x2) for i in range(4,10): r=i*delta_L x2=Q1*(4*delta_L)**3/(3*r) print('z=',r,' V=',x2) for i in range(1,5): r=i*delta_L x2=Q1*r/3 print('z=',r,'Ez=',x2) for i in range(4,10): r=i*delta_L x2=Q1*(4*delta_L)**3/3/r**2 print('z=',r,'Ez=',x2) for I in range(500): #2000 pd = p.copy() p[1:-1,1:-1,1:-1] = (pd[2:,1:-1,1:-1] + pd[:-2,1:-1,1:-1] + pd[1:-1,2:,1:-1] + pd[1:-1,:-2,1:-1] + pd[1:-1,1:-1,2:] + pd[1:-1,1:-1,:-2] + (delta_L**2)*charge[1:-1, 1:-1, 1:-1]) /6 p[0][:][:]=0 p[nx-1][:][:]=0 p[:][0][:]=0 p[:][ny-1][:]=0 p[:][:][0]=0 p[:][:][nz-1]=0 def plot2D(x, y, p): fig = plt.figure(figsize=(11, 7), dpi=100) ax = fig.gca(projection='3d') X, Y = np.meshgrid(x, y) surf = ax.plot_surface(X, Y, p[:], rstride=1, cstride=1, cmap=cm.viridis,linewidth=0, antialiased=False) ax.view_init(30, 225) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_zlabel('p') plot2D(x,y,p[:,:,HLN]) def plot_surface(pp): nx = LN ny = LN fig = plt.figure(figsize=(11,7), dpi=100) fig xmin = 0 xmax = LN-1 ymin = 0 ymax = LN-1 x = np.linspace(xmin, xmax, nx) y = np.linspace(ymin, ymax, ny) X, Y = np.meshgrid(x, y) plt.contourf(X, Y, pp, alpha=0.1, cmap=cm.viridis) plt.colorbar() plt.xlabel('X') plt.ylabel('Y') plot_surface(p[:,:,HLN]) plt.xlim([HLN-5,HLN+5]) plt.ylim([HLN-5,HLN+5]) plt.style.use('ggplot') plt.rcParams["axes.facecolor"] = 'white' fig = plt.figure() ax = fig.gca(projection='3d') L=9 cs=p[HLN-4:HLN+8,HLN-4:HLN+8,HLN-4:HLN+8] X, Y, Z= np.meshgrid(np.arange(0, L,1), np.arange(0, L,1), np.arange(0, L,1)) Ex = np.zeros([L,L,L]) Ey = np.zeros([L,L,L]) Ez = np.zeros([L,L,L]) EE = np.zeros([L,L,L]) for i in range(L): for j in range(L): for k in range(L): Ex[i,j,k]=-(cs[i,j+1,k]-cs[i,j-1,k]) Ey[i,j,k]=-(cs[i+1,j,k]-cs[i-1,j,k]) Ez[i,j,k]=-(cs[i,j,k+1]-cs[i,j,k-1]) for i in range(L): for j in range(L): for k in range(L): ii=i*2+HLN-8 jj=j*2+HLN-8 kk=k*2+HLN-8 Ex[i,j,k]=-(p[ii,jj+1,kk]-p[ii,jj-1,kk])/2/delta_L#*2#*0.004#*10 Ey[i,j,k]=-(p[ii+1,jj,kk]-p[ii-1,jj,kk])/2/delta_L#*2#*0.004#*10 Ez[i,j,k]=-(p[ii,jj,kk+1]-p[ii,jj,kk-1])/2/delta_L#*2#*0.004#*10 print("V=") for i in range(1,12): print('z=',i*delta_L,'Vz=',p[HLN,HLN,HLN+i]) i=25 k=25 for j in range(26,35): print("x=",(j-25)*delta_L,"Ex=",-(p[i,j+1,k]-p[i,j-1,k])/2/delta_L) ax.quiver(X,Y,Z, Ex, Ey, Ez, color='red',length=0.5*delta_L**2, normalize=False) #ax.quiver(X,Y,Z, Ex, Ey, Ez, color='red',length=0.2*(delta_L**2), normalize=False) for i in range(-2,2): for j in range(-2,2): for k in range(-2,2): X1,Y1, Z1=int(L/2)+i,int(L/2)+j,int(L/2)+k ax.scatter3D(X1,Y1,Z1,"o", color='blue') for i in range(5): X1,Y1, Z1=int(L/2)-i,int(L/2),int(L/2) ax.scatter3D(X1,Y1,Z1,"o", color='blue') X1,Y1, Z1=int(L/2)+i,int(L/2),int(L/2) ax.scatter3D(X1,Y1,Z1,"o", color='blue') X1,Y1, Z1=int(L/2),int(L/2)-i,int(L/2) ax.scatter3D(X1,Y1,Z1,"o", color='blue') X1,Y1, Z1=int(L/2),int(L/2)+i,int(L/2) ax.scatter3D(X1,Y1,Z1,"o", color='blue') X1,Y1, Z1=int(L/2),int(L/2),int(L/2)-i ax.scatter3D(X1,Y1,Z1,"o", color='blue') X1,Y1, Z1=int(L/2),int(L/2),int(L/2)+i ax.scatter3D(X1,Y1,Z1,"o", color='blue') # グラフ描画 plt.grid() plt.draw() plt.show() 5. 計算結果-delta_T=1.0の場合  図2(a)にz=25面の電位分布の2Dプロット図を,図2(b)に電場図を示す.                図2(a)電位分布                               図2(b)電場   6. 解析の妥当性検証 6.1円板状電荷周りの電位,電場の理論的計算 図3(a)(b)に理論計算のためのモデルを示す.   図3(a) 理論計算のためのモデル r>=aの場合 理論値は下記で計算できる.   図3(b) 理論計算のためのモデル r<aの場合 理論値は下記で計算できる. 6.2 電位,電場の理論値と計算値の比較 表1にdelta_L=1.0の場合の電位,電場の理論値と計算値の比較を示す. 計算値は理論値にほぼ一致している.     表1 delta_L=1.0の場合の電位,電場の理論値と計算値の比較 参考文献 (1)Python三次元ポアソン方程式による静電場解析 (1)点電荷の電場解析 Qiita (2)Python三次元ポアソン方程式による静電場解析 (2)円板状電荷の電場解析 Qiita
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

各桁の和をpython, C++で実行する

各桁の和をpython, C++で実装するためのメモです。 普段はpythonをメインに使っていますが、勉強中のC++でどうかけるかを考えたので共有したいと思います。 pythonの場合 pythonであれば、以下のように短くかけて簡単です。 List = list(map(int, str(n)))) ans = sum(List) ただし、nは入力した数字を指します。 1行目では文字列にしたnに対して、各位の数字を一個ずつint型に変換してlistに格納します。その後、2行目でsum関数を使って総和をとるという形です。 例えばn=12345という数字の場合、 List = [1,2,3,4,5] #1行目 List = list(map(int, str(n))) の結果 ans = 15 #2行目 ans = sum(List) の結果 という感じです。 C++の場合(イテレータを使う方法) 上記の内容と同じことをC++でもやりたいと思います。すなわち、文字列の各位をintに直して足していくというものです。 int digit_sum(string n){ int sum = 0; for(auto itr=n.begin();itr < n.end();itr++){ sum += *itr - '0'; // *itrはchar型のため、数値に直すには'0'で引き算する。 } return sum; } int main(){ string n= "12345"; cout << digit_sum(n) << endl; } ここではイテレータitrを使いました。数字列の各位の値は*itrで取得できます。ただし注意として、各位の値はchar型として得られるので、整数の数値に直す必要があります。その操作が sum += *itr - '0'; に相当します。 C++を用いた別の方法(イテレータを使わない方法) 以下のような求め方の記事をしばしば目にします。 int digit_sum(int n){ int sum = 0; while(n){ sum += n%10; n /= 10; } return sum; } 例えばn=123の場合、while文で以下のような操作が実行されます。 123%10 = 3 -> sumに追加 123/10 = 12 12%10 = 2 -> sumに追加 12/10 = 1 1%10 = 1 -> sumに追加 1/10 = 0 //終了 こっちはイテレータを使わないので、イテレータに慣れてない僕にとってはわかりやすかったです。 まとめ 各位の和を求める方法をまとめました。単純な内容でしたが、色々な解法を見れて楽しかったですし、イテレータに慣れる良い機会になりました。 少しでも有益になれば幸いです。また、間違いや不正確な部分があれば教えていただけると幸いです。 参考 char型をint型に変換する方法と注意【数値化 キャスト 文字列変換】 各桁の和の求め方
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DataFrameの計算でWarningが出た!その理由は

DataFrame同士で計算を行っていると、次のWarningが出る事があります。 このエラーは、例えば、「カラム名を同じもので上書きしようとした場合」に出るはずです。 A value is trying to be set on a copy of a slice from a DataFrame. Try using .loc[row_indexer,col_indexer] = value instead See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy Warningの意味は、「もとのDataFrameからコピーした。(=もとのDFは上書きしなかった)」というものです。 しかし、欲しい結果は得られているのではないでしょうか。 結果は正しいのですが、Pythonは「この結果は、あなたが望んだ結果ではないかもしれないので、注意して」と言っているのです。 ※ここではDataFrameを「df」(またはDF)と表記します。 ※ここで使用したcsvファイル、コード全文は、ページ下部の参考ページのリンクから参照可能です。 内容 どんな時に起きるか 原因 解決方法 参考ページ https://shilabo.com/python/web_self/ https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html 【どんな時に起きるか】 これは、例えば、%表示の変換で、「1.00(%)」となっているものを数値「0.01」に同じカラム名で変換(上書き)しようと、 df2['pct'] = df2['pct'] / 100 と書いた場合に起きると思います。それは、コードを辿ると、df2が他のDFを参照し df2 = df1[['clm0', 'clm1']] のように書いているのではないでしょうか? 【原因】 これは chained indexing と呼ばれるものです。 dfを連鎖的にカラム名を指定して参照(複製)した場合に起きます。 例えば、df1が所与の時、新しいdf2に対して、 df2 = df1[['clm0', 'clm1']]と書いて、更にdf2['clm0'] =1のように、 辿った先の、元のdf1を、df1のカラム名を使って上書きする(上書きするのと同じコードを書いた)ような場合に起きます。 df2 = df1と書けば、df2['clm0']=1(カラム’clm0’を全部1にする)と書いても問題は起きません。それは、df1の全てがdf2に複製され、df2をいじると、df1も同時に上書きされるからです。 ところが、カラム名を指定して、df2 = df1[['clm0', 'clm1']]と書くと、df2には写しが渡されますが、ここでdf1に存在するカラム名’clm0’を指定してdf2の方からdf2['clm0']=1と書くと、Pythonはdf1は上書きしない、という判断をします。 本来、df2 = df1という書き方は、df2をいじれば、df1も上書きする、という仕様です。しかし、df2['clm0']=1と書くと、この仕様とは異なるので、Warningを出すと思われます。 Warningの指示通り、df.loc[]を使っても(例えば、df.loc[:,'pct'])、同じWarningが出るはずです。 まず、仕様を確認します。 import pandas as pd f1 = r'C:\Users\shilabo\Documents\SHiLABO_python\a004_007a.csv' df1 = pd.read_csv(f1) #1.1(a) example df2A = pd.read_csv(f1) df2a = df2A df2a['clm0'] = 99 print('#df2a\n', df2a) #df2a # clm0 clm1 clm2 clm3 #0 99 -1 12 105 #1 99 -2 22 205 #2 99 -3 32 305 #3 99 -4 42 405 #4 99 -5 52 505 print('#df2A\n', df2A) #df2A # clm0 clm1 clm2 clm3 #<=df2Aも'clm0'が上書きされる。 #0 99 -1 12 105 #1 99 -2 22 205 #2 99 -3 32 305 #3 99 -4 42 405 #4 99 -5 52 505 view rawa004_007_02.py hosted with ❤ by GitHub 次にWaningを再現します。 df2B = pd.read_csv(f1) df2b = df2B[['clm0', 'clm1']] df2b['clm0'] = 99 print('#df2b\n',df2b)  #df2b #<=df2bはWarningが出て、上書きされる。 # clm0 clm1 #0 99 -1 #1 99 -2 #2 99 -3 #3 99 -4 #4 99 -5 #A value is trying to be set on a copy of a slice from a DataFrame. #Try using .loc[row_indexer,col_indexer] = value instead print('#df2B\n',df2B) #df2B # clm0 clm1 clm2 clm3 #<=df2Bは'clm0'が上書きされない。 #0 1 -1 12 105 #1 2 -2 22 205 #2 3 -3 32 305 #3 4 -4 42 405 #4 5 -5 52 505 【解決方法】 同じ名前のカラム名で、結果を入れた新しいカラムを作るのを「避ければ」、このWarningは回避できます。 または、 同じカラム名にしたければ、以下のように、df2 = df1['clm0'].copy()と書いて、コピーである事を明確にすれば、Warningは出ません。 df2C = pd.read_csv(f1) df2c = df2C[['clm0', 'clm1']].copy() #.copy()とする。 df2c['clm0'] = 99 print('#df2c\n',df2c) #df2c # clm0 clm1 #<=df2cで'clm0'を上書きしてもWarningは出ない。 #0 99 -1 #1 99 -2 #2 99 -3 #3 99 -4 #4 99 -5 print('#df2C\n',df2C) #df2C # clm0 clm1 clm2 clm3 #<=df2Cは上書きされない。 #0 1 -1 12 105 #1 2 -2 22 205 #2 3 -3 32 305 #3 4 -4 42 405 #4 5 -5 52 505 勿論、Warningの内容を分かっていて使っていれば、多くの場合、問題ありません。 参考ページ 実践で使うPythonの技術が掲載されています。書籍のように構成されています。 pandasの該当部分のドキュメント(英語)です。 https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html コードに用いたcsvファイル(GitHub) : a004_007a.csv ここに掲載したコード全文
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Fashion Classification】Pytorch VGG19 modelを使ってFashion Imageを分類

Fashion Classification紹介 Fashionデータを利用してカバン、シャツ、スカートなどイメージを分類しました。 構成 データパイプライン : データをロードする時に負担を緩和するためにパイプライン構築 早めに学習終了設定 : 学習改善があまり進めても出来ない場合は早めに学習終了を設定する。 TensorBoard設定 : TensorBoardと連結して学習進行状態を見る。 ディレクトリ 以下のようにdatasetディレクトリにバッグ、シャツ、スカートなどフォルダが分かれてるし、画像ファイルがあります。 ├─dataset │ ├─bag │ ├─shirt │ └─skirt └─runs └─fashion_classification データをロード import os import time from PIL import Image import numpy as np import matplotlib.pyplot as plt import random import torch import torch.nn as nn from torch import optim, cuda from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter from torchvision import datasets, models, transforms from torchvision.utils import make_grid import matplotlib.pyplot as plt from torchsummary import summary BASE_DIR = os.getcwd() DATASET_PATH = os.path.join(BASE_DIR, 'dataset') MODE_PATH = os.path.join(BASE_DIR, 'models') npyファイルをチェックする理由は最初の訓練時はイメージでnpyファイルを生成し次の訓練の時からnpyファイルをロードして時間を節約するため。 classes = [] dataset = [] for fullpath, dirnames, filenames in os.walk(DATASET_PATH): if DATASET_PATH == fullpath: classes = dirnames continue label_name = fullpath.split(os.path.sep)[-1] for filename in filenames: if filename.find('.npy') != -1: continue class_index = classes.index(label_name) dataset.append((os.path.join(fullpath, filename), class_index)) random.shuffle(dataset) total_len = len(dataset) train_ratio = int(total_len * 0.8) train_data = dataset[:train_ratio] test_data = dataset[train_ratio:] データパイプライン すべてのデータを取得してメモリが負荷することがある。そのために訓練時バッチサイズだけ取得してデータを加工する。 最初の訓練時のイメージのパスでnpyファイルを作成し、次の訓練の時からnpyファイルだけをロードして、時間をデータの読み込み時間を節約する。 class Dataset(object): def __init__(self, transforms, dataset): self.transforms = transforms self.dataset = dataset def __getitem__(self, idx): img_path, label = self.dataset[idx] fullpath, extension = os.path.splitext(img_path) npy_path = fullpath + '.npy' if os.path.exists(npy_path): np_data = np.load(npy_path) img = Image.fromarray(np.uint8(np_data)) else: img = Image.open(img_path).convert("RGB") np_data = np.asarray(img) np.save(npy_path, np_data) if self.transforms is not None: img = self.transforms(img) return img, label def __len__(self): return len(self.dataset) transform = transforms.Compose( [ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ] ) 訓練および評価データパイプラインを生成する。 train_dataset = Dataset(transform, train_data) test_dataset = Dataset(transform, test_data) train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=20) test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=5) データ視覚化 イメージ一つを視覚化して見る。 dataloader = iter(train_loader) imgs, target = dataloader.next() tf = transforms.ToPILImage() pil_img = tf(imgs[0]) plt.figure() plt.imshow(pil_img) plt.colorbar() plt.gca().grid(False) モデル設定 vgg19モデルを参考。 learning_rate = 0.001 device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu') device class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv = nn.Sequential( nn.Conv2d(3, 64, 3, padding=1),nn.LeakyReLU(0.2), nn.Conv2d(64, 64, 3, padding=1),nn.LeakyReLU(0.2), nn.MaxPool2d(2, 2), nn.Conv2d(64, 128, 3, padding=1),nn.LeakyReLU(0.2), nn.Conv2d(128, 128, 3, padding=1),nn.LeakyReLU(0.2), nn.MaxPool2d(2, 2), nn.Conv2d(128, 256, 3, padding=1),nn.LeakyReLU(0.2), nn.Conv2d(256, 256, 3, padding=1),nn.LeakyReLU(0.2), nn.Conv2d(256, 256, 3, padding=1),nn.LeakyReLU(0.2), nn.MaxPool2d(2, 2), nn.Conv2d(256, 512, 3, padding=1),nn.LeakyReLU(0.2), nn.Conv2d(512, 512, 3, padding=1),nn.LeakyReLU(0.2), nn.Conv2d(512, 512, 3, padding=1),nn.LeakyReLU(0.2), nn.MaxPool2d(2, 2), nn.Conv2d(512, 512, 3, padding=1),nn.LeakyReLU(0.2), nn.Conv2d(512, 512, 3, padding=1),nn.LeakyReLU(0.2), nn.Conv2d(512, 512, 3, padding=1),nn.LeakyReLU(0.2), nn.MaxPool2d(2, 2) ) self.avg_pool = nn.AvgPool2d(7) self.classifier = nn.Linear(512, 10) def forward(self, x): features = self.conv(x) x = self.avg_pool(features) x = x.view(features.size(0), -1) x = self.classifier(x) return x, features model = Net().to(device) model Net( (conv): Sequential( (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (1): LeakyReLU(negative_slope=0.2) (2): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (3): LeakyReLU(negative_slope=0.2) (4): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (5): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (6): LeakyReLU(negative_slope=0.2) (7): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (8): LeakyReLU(negative_slope=0.2) (9): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (10): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (11): LeakyReLU(negative_slope=0.2) (12): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (13): LeakyReLU(negative_slope=0.2) (14): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (15): LeakyReLU(negative_slope=0.2) (16): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (17): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (18): LeakyReLU(negative_slope=0.2) (19): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (20): LeakyReLU(negative_slope=0.2) (21): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (22): LeakyReLU(negative_slope=0.2) (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) (24): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (25): LeakyReLU(negative_slope=0.2) (26): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (27): LeakyReLU(negative_slope=0.2) (28): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) (29): LeakyReLU(negative_slope=0.2) (30): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False) ) (avg_pool): AvgPool2d(kernel_size=7, stride=7, padding=0) (classifier): Linear(in_features=512, out_features=10, bias=True) ) criterion = torch.nn.CrossEntropyLoss().to(device) optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate) summary(model.to(device), (3, 224, 224)) ---------------------------------------------------------------- Layer (type) Output Shape Param # ================================================================ Conv2d-1 [-1, 64, 224, 224] 1,792 LeakyReLU-2 [-1, 64, 224, 224] 0 Conv2d-3 [-1, 64, 224, 224] 36,928 LeakyReLU-4 [-1, 64, 224, 224] 0 MaxPool2d-5 [-1, 64, 112, 112] 0 Conv2d-6 [-1, 128, 112, 112] 73,856 LeakyReLU-7 [-1, 128, 112, 112] 0 Conv2d-8 [-1, 128, 112, 112] 147,584 LeakyReLU-9 [-1, 128, 112, 112] 0 MaxPool2d-10 [-1, 128, 56, 56] 0 Conv2d-11 [-1, 256, 56, 56] 295,168 LeakyReLU-12 [-1, 256, 56, 56] 0 Conv2d-13 [-1, 256, 56, 56] 590,080 LeakyReLU-14 [-1, 256, 56, 56] 0 Conv2d-15 [-1, 256, 56, 56] 590,080 LeakyReLU-16 [-1, 256, 56, 56] 0 MaxPool2d-17 [-1, 256, 28, 28] 0 Conv2d-18 [-1, 512, 28, 28] 1,180,160 LeakyReLU-19 [-1, 512, 28, 28] 0 Conv2d-20 [-1, 512, 28, 28] 2,359,808 LeakyReLU-21 [-1, 512, 28, 28] 0 Conv2d-22 [-1, 512, 28, 28] 2,359,808 LeakyReLU-23 [-1, 512, 28, 28] 0 MaxPool2d-24 [-1, 512, 14, 14] 0 Conv2d-25 [-1, 512, 14, 14] 2,359,808 LeakyReLU-26 [-1, 512, 14, 14] 0 Conv2d-27 [-1, 512, 14, 14] 2,359,808 LeakyReLU-28 [-1, 512, 14, 14] 0 Conv2d-29 [-1, 512, 14, 14] 2,359,808 LeakyReLU-30 [-1, 512, 14, 14] 0 MaxPool2d-31 [-1, 512, 7, 7] 0 AvgPool2d-32 [-1, 512, 1, 1] 0 Linear-33 [-1, 10] 5,130 ================================================================ Total params: 14,719,818 Trainable params: 14,719,818 Non-trainable params: 0 ---------------------------------------------------------------- Input size (MB): 0.57 Forward/backward pass size (MB): 218.40 Params size (MB): 56.15 Estimated Total Size (MB): 275.12 ---------------------------------------------------------------- class EarlyStopping: """Early stops the training if validation loss doesn't improve after a given patience.""" def __init__(self, patience=10, verbose=False, delta=0, path='checkpoint.pt', trace_func=print): """ Args: patience (int): How long to wait after last time validation loss improved. Default: 7 verbose (bool): If True, prints a message for each validation loss improvement. Default: False delta (float): Minimum change in the monitored quantity to qualify as an improvement. Default: 0 path (str): Path for the checkpoint to be saved to. Default: 'checkpoint.pt' trace_func (function): trace print function. Default: print """ self.patience = patience self.verbose = verbose self.counter = 0 self.best_score = None self.early_stop = False self.val_loss_min = np.Inf self.delta = delta self.path = path self.trace_func = trace_func def __call__(self, val_loss, model): score = -val_loss if self.best_score is None: self.best_score = score self.save_checkpoint(val_loss, model) elif score < self.best_score + self.delta: self.counter += 1 self.trace_func(f'EarlyStopping counter: {self.counter} out of {self.patience}') if self.counter >= self.patience: self.early_stop = True else: self.best_score = score self.save_checkpoint(val_loss, model) self.counter = 0 def save_checkpoint(self, val_loss, model): '''Saves model when validation loss decrease.''' if self.verbose: self.trace_func(f'Validation loss decreased ({self.val_loss_min:.6f} --> {val_loss:.6f}). Saving model ...') torch.save(model.state_dict(), self.path) self.val_loss_min = val_loss writer = SummaryWriter('runs/fashion_classification') def matplotlib_imshow(img, one_channel=False): if one_channel: img = img.mean(dim=0) img = img / 2 + 0.5 npimg = img.cpu().numpy() if one_channel: plt.imshow(npimg, cmap="Greys") else: plt.imshow(np.transpose(npimg, (1, 2, 0))) def images_to_probs(model, images): output,f = model(images) _, preds_tensor = torch.max(output, 1) preds = np.squeeze(preds_tensor.cpu().numpy()) return preds, [nn.functional.softmax(el, dim=0)[i].item() for i, el in zip(preds, output)] def plot_classes_preds(model, images, labels): preds, probs = images_to_probs(model, images) fig = plt.figure(figsize=(12, 48)) for idx in np.arange(4): if len(images) - 1 < idx: break ax = fig.add_subplot(1, 4, idx+1, xticks=[], yticks=[]) matplotlib_imshow(images[idx], one_channel=True) ax.set_title("{0}, {1:.1f}%\n(label: {2})".format( classes[preds[idx]], probs[idx] * 100.0, classes[labels[idx]]), color=("green" if preds[idx]==labels[idx].item() else "red")) return fig 学習(訓練) def train_model(model, n_epochs): train_losses = [] valid_losses = [] early_stopping = EarlyStopping(verbose = True) for epoch in range(n_epochs): epoch_loss = 0 start = time.time() model.train() for batch, (data, target) in enumerate(train_loader, 1): data = data.to(device) target = target.to(device) output, f = model(data) loss = criterion(output, target) optimizer.zero_grad() loss.backward() optimizer.step() epoch_loss += loss if batch % 100 == 0: writer.add_scalar('training loss', epoch_loss / 100, epoch * len(train_loader) + batch) writer.add_figure('predictions vs. actuals', plot_classes_preds(model, data, target), global_step=epoch * len(train_loader) + batch) print(f'epoch : {epoch+1}, Loss : {epoch_loss}, time : {time.time() - start}') model.eval() for data, target in test_loader : data = data.to(device) target = target.to(device) output, f = model(data) loss = criterion(output, target) valid_losses.append(loss.item()) train_loss = np.average(train_losses) valid_loss = np.average(valid_losses) epoch_len = len(str(n_epochs)) train_losses = [] valid_losses = [] early_stopping(valid_loss, model) if early_stopping.early_stop: print("Early stopping") break n_epochs = 50 train_model(model, n_epochs) Tensorboard観察 tensorboard --logdir runs 予測 columns = 5 rows = 3 fig = plt.figure(figsize=(20,10)) model.eval() for i in range(1, columns*rows+1): idx = np.random.randint(len(test_data)) img_path, class_index = test_data[idx] pil_img = Image.open(img_path).convert('RGB') img = transform(pil_img).unsqueeze(dim=0).to(device) output,f = model(img) _, argmax = torch.max(output, 1) pred = classes[argmax.item()] label = classes[class_index] fig.add_subplot(rows, columns, i) plt.title(pred) plt.imshow(pil_img) plt.axis('off') plt.show()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ラズパイからデータを送る時に起こった事

はじめに pythonでラズパイからlocalhostのサーバにデータを送って色々と処理しようとしたときに、そもそもデータ送れないってなって困ってたのでその内容。 そもそも単純に送るならどうする? JSON送るならこんな感じ import requests import json #POST先URL url = "POST送信するURL" #JSON形式のデータ jsonData = { "col1": "val1", "col2": "val2" } #POST送信 response = requests.post( url, json = json.dumps(jsonData) #jsonはなくても送信可能 ) resDatas = response.json() URL指定して後は終わりーって思ってたのに遅れない 理由 WindowsでWSL使ってたのだが 実際に入力していたURLはWindowsのip元にしたURLでした WSL使ってると WindowsのIPアドレスとWSLの二つのIPが生まれる 外部からlocalhostに接続しようと思うと 外部端末→WSLのIP→WindowsのIP っていう順番になる。 外部接続の窓口はWSLのIP使う 今回僕はWindowsのIP使ったURLの方を使ってたから一つ段階とばしてる状態になってた。 ちゃんと窓口通さんと通信させへんぞ!!って怒られてるイメージ ちなみに両方のipはコマンドプロンプトからipconfigで確認できる。 おそらくmacだとこうはならずに普通にIP通したら行けるはず。 なんかめんどくさいなwsl
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flat UI ColorsのWebサイトをスクレイピングしてRGB値を抽出してみよう

はじめに 僕は普段アプリの配色を考えるときにFlat UI Colors (https://flatuicolors.com/)を参考にしています。このサイトの配色に従うことで、色の選択に悩むことが少なくなり、整った印象を持つアプリが作れるようになります。 このサイトはワンクリックでHEX値やRGB値などをコピーできるように作られていて便利なのですが、それでも毎回サイトからコピーするのはわりと面倒です。 「エンジニアならもっと楽したい…。ならば、Flat UI ColorsをスクレイピングしてRGB値を抽出し、ソースコードファイルに変換してしまおう」 という発想です。 この記事で紹介するソースコードはGitHub (https://github.com/HituziANDO/flat_ui_colors)にプッシュしていますので、あわせてご参照ください。 スクレイピングの成果物 まず最初に、スクレイピングの成果物は以下のようなRGB値を列挙したソースコードです。以下は成果物の一つのJavaScriptコードの一部です。完全版はこちらにあります。他にもTypeScript、SCSS、Swiftに対応しています。 flat_ui_colors.jsの一部 /** * Flat UI Palette v1 by Flat UI Colors * https://flatuicolors.com/palette/defo */ const Defo = { turquoise: rgb(26, 188, 156), emerald: rgb(46, 204, 113), peterRiver: rgb(52, 152, 219), amethyst: rgb(155, 89, 182), wetAsphalt: rgb(52, 73, 94), greenSea: rgb(22, 160, 133), nephritis: rgb(39, 174, 96), belizeHole: rgb(41, 128, 185), wisteria: rgb(142, 68, 173), midnightBlue: rgb(44, 62, 80), sunFlower: rgb(241, 196, 15), carrot: rgb(230, 126, 34), alizarin: rgb(231, 76, 60), clouds: rgb(236, 240, 241), concrete: rgb(149, 165, 166), orange: rgb(243, 156, 18), pumpkin: rgb(211, 84, 0), pomegranate: rgb(192, 57, 43), silver: rgb(189, 195, 199), asbestos: rgb(127, 140, 141), } const FlatUIColors = { Defo, }; ディレクトリ構成 プロジェクトのディレクトリ構成は以下の通りです。 L app/ L __init__.py L main.py L flat_ui_colors_parser.py L writer/ L __init__.py L js_writer.py L scss_writer.py L swift_writer.py L ts_writer.py L util/ L __init__.py L string_util.py L dist/ L tmp/screenshots/ L requirements.txt L README.md appディレクトリがPythonコードを格納しているところになり、main.pyがエントリポイントとなります。ターミナルでpython app/main.pyと打つことでスクレイピングを開始できます。distとtmpディレクトリはプログラムを実行することで生成され、スクレイピングの結果が出力されます。 flat_ui_colors_parser.pyがスクレイピングおよびHTMLパーサの実装になります。writerディレクトリには、スクレイピングで得たデータを各プログラミング言語のソースコードに変換するWriterが実装されています。 プロジェクトのセットアップに関しては、README.mdを読んでください。 プログラムの解説 このプログラムは主にMain、Parser、Writerの3つのパートからなります。 Main エントリポイントとなるmain.pyはシンプルです。以下の処理をしています。 Writerの初期化 WebサイトのURLをfor文で順番にFlatUiColorsParserに渡し実行 Writerの終了処理および出力 main.py import os from flat_ui_colors_parser import FlatUiColorsParser from writer.js_writer import JsWriter from writer.scss_writer import ScssWriter from writer.swift_writer import SwiftWriter from writer.ts_writer import TsWriter if __name__ == '__main__': # Writerの初期化 writers = [SwiftWriter(), JsWriter(), TsWriter(), ScssWriter()] urls = [ "https://flatuicolors.com/palette/defo", "https://flatuicolors.com/palette/us", "https://flatuicolors.com/palette/au", "https://flatuicolors.com/palette/gb", "https://flatuicolors.com/palette/ca", "https://flatuicolors.com/palette/cn", "https://flatuicolors.com/palette/nl", "https://flatuicolors.com/palette/fr", "https://flatuicolors.com/palette/de", "https://flatuicolors.com/palette/in", "https://flatuicolors.com/palette/ru", "https://flatuicolors.com/palette/es", "https://flatuicolors.com/palette/se", "https://flatuicolors.com/palette/tr" ] for url in urls: FlatUiColorsParser.exec(url, writers) # Writerの終了処理 for writer in writers: writer.finish() # distディレクトリがない場合は作成 DIST_DIR = "dist" os.makedirs(DIST_DIR, exist_ok=True) # 結果の出力 for writer in writers: f = open(f"{DIST_DIR}/{writer.filename()}", "w", encoding="UTF-8") f.write(writer.source_code()) f.close() Parser 次にFlatUiColorsParserクラスを見ていきます。このクラスはスクレイピングとHTML解析を担当しています。 flat_ui_colors_parser.py import os import re import time from bs4 import BeautifulSoup from selenium import webdriver from selenium.webdriver import Chrome from util.string_util import to_camel class FlatUiColorsParser: @classmethod def exec(cls, url, writers): # URLからリスト名を抽出 # 例) https://flatuicolors.com/palette/defo -> Defo list_name = to_camel(url.rsplit('/', 1)[-1], True) # ヘッドレスモードでChromeを動かす opts = webdriver.ChromeOptions() opts.add_argument("--headless") opts.add_argument("--no-sandbox") driver = Chrome(executable_path="/usr/local/bin/chromedriver", options=opts) # サイトを取得 driver.get(url) # Flat UI ColorsのサイトはVue.jsで画面が作られるため、 # ページ取得およびJS実行が終わるまで少し待機 time.sleep(4) # HTMLを取得 html = driver.page_source # 色見本用にサイトのスクリーンショットを撮っておく os.makedirs("tmp/screenshots", exist_ok=True) driver.save_screenshot(f"tmp/screenshots/{list_name}.png") # webdriverの終了 driver.quit() # HTML解析にBeautifulSoup4を使用 soup = BeautifulSoup(html, 'html.parser') # このカラーパレットを作ったデザイナー名を抽出 authors = [] elements = soup.select(".author") for element in elements: author = element.text.replace("\n", "").strip() authors.append(author) # RGB情報を抽出 colors = [] # class="color"の要素をすべて取得 elements = soup.select(".color") for element in elements: # 色名を取得。余計な文字は除去し、キャメルケースに変換 color_name = element.text \ .replace('\'', '') \ .replace('-', ' ') \ .replace('!', '') \ .replace('?', '') \ .replace('=', '') \ .strip() color_name = to_camel(color_name, False) # 色名は後で変数名に使うため、 # 数字から始まる色名は先頭に'_'を付ける if re.match(r'^\d.*$', color_name): color_name = '_' + color_name # アルファベット以外の文字を置換 color_name = color_name \ .replace('á', 'a') \ .replace('ó', 'o') \ .replace('è', 'e') \ .replace('ā', 'a') \ .replace('â', 'a') # 要素のstyle属性に"background: rgb(r, g, b);"の形式でRGB値が入っているので、 # rgb(r, g, b)だけを抽出 color = element.get("style").replace('background: ', '').replace(';', '').strip() colors.append((color_name, color)) # Writerに抽出した情報を渡す for writer in writers: writer.add_color_list(url, authors, list_name, colors) Writer 最後にWriterを見ていきます。ここではJsWriterクラスを使って説明します。このクラスはFlatUIColorsParserクラスの解析結果を受け取って、JavaScriptのソースコードに変換する役目を担っています。 js_writer.py class JsWriter: """ JavaScript code writer. """ def __init__(self, indent=" "): self.__list_names = [] self.__indent = indent # JavaScriptコード # RGB値を扱う基本クラスやユーティリティ関数を定義 self.__source_code = """export class FlatUIColor { /** * @param {number} red Red. The range of its value is 0 to 255. * @param {number} green Green. The range of its value is 0 to 255. * @param {number} blue Blue. The range of its value is 0 to 255. */ constructor(red, green, blue) { this.red = red; this.green = green; this.blue = blue; } /** * Returns rgb(r, g, b). */ rgbAsCSS() { return `rgb(${this.red}, ${this.green}, ${this.blue})`; } /** * Returns rgba(r, g, b, a). * @param {number} alpha Alpha. The range of its value is 0 to 1.0. */ rgbaAsCSS(alpha) { return `rgba(${this.red}, ${this.green}, ${this.blue}, ${alpha})`; } } const rgb = (r, g, b) => new FlatUIColor(r, g, b);\n\n""" def filename(self): # 出力されるファイル名 return "flat_ui_colors.js" def source_code(self): # JsWriterで書き込まれたJSコード return self.__source_code def add_color_list(self, url, authors, list_name, colors): """ :param url: URL of the color list page. :param authors: Authors. :param list_name: The color list name. :param colors: (color_name, color)[]. color_name: A color name. color: 'rgb(r, g, b)' """ # コメントの追加 # このカラーパレットを作ってくれたデザイナーに敬意を払ってauthorは書きましょう self.__source_code += "/**\n" for author in authors: self.__source_code += f" * {author}\n" self.__source_code += f" * {url}\n" self.__source_code += " */\n" # begin object {list_name} self.__source_code += f"const {list_name} = {{\n" # Add colors for color in colors: self.__add_color(color[0], color[1]) self.__source_code += "\n" # begin array self.__source_code += f"{self.__indent}palette: [\n" for color in colors: self.__source_code += f"{self.__indent}{self.__indent}{list_name}.{color[0]},\n" # end array self.__source_code += f"{self.__indent}]\n" # end object {list_name} self.__source_code += "};\n\n" self.__list_names.append(list_name) def finish(self): # begin export default object self.__source_code += "const FlatUIColors = {\n" for name in self.__list_names: self.__source_code += f"{self.__indent}{name},\n" # end export default object self.__source_code += "};\n" self.__source_code += "export default FlatUIColors;\n" def __add_color(self, color_name, color): """ :param color_name: A color name. :param color: rgb(r, g, b) """ self.__source_code += f"{self.__indent}{color_name}: {color},\n" 見ての通りFlatUIColorsParserから受け取った情報をもとに完成形をイメージしてJavaScriptコードを作っていきます。このJsWriterで出力されるソースコードは、スクレイピングの成果物で紹介したものになります。 Writerは、filename、source_code、add_color_list、finishメソッドを実装しておけば、ダックタイピングにより様々なWriterを動かすことができます。つまり、このプロジェクトに用意されていないプログラミング言語でも、他のWriterを参考に書き、main.pyに追加すれば、同様に出力を得ることができます。 おわりに 上記のJsWriterで出力されたflat_ui_colors.jsは、以下のように使うことができます。僕は、CSS in JSで使うことが多いため、rgbAsCSSというメソッドを生やしています。 JavaScript const color = FlatUIColors.Gb.protossPylon.rgbAsCSS() console.log(color) // -> "rgb(0, 168, 255)" 最後に、スクレイピング・プログラムを回しまくるのは控えましょう。成果物もGitHubにプッシュしてあるので、皆さんはここからコピーして使ってください。もし、使っているプログラミング言語のソースコードファイルが無いときは、Writerを書いてプルリクくだされば有り難いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SPSS Modelerの決定木ノードをPythonで書き換える。信用リスクの判定

SPSS Modelerの決定木ノードのCARTをPythonで書き換えます。 0.データ 以下のようなデータを用いて決定木モデルを作ります。 目的変数 Credit_rating:信用リスク 説明変数 Age:年齢 Income:収入ランク Credit_cards:クレジットカード枚数 Education:学歴 Car_loans:車のローン数 年齢や収入ランクから信用リスクを判定するモデルを作ります。 1m.①説明変数、目的変数の定義 Modeler版 まず、データ型ノードで説明変数、目的変数の定義を行う必要があります。 「値の読み込み」ボタンをクリックし、Credit_ratingのロールを「対象」(目的変数の意味)を設定します。Age、Income、Credit_cards、Education、Car_loansは「入力」(説明変数の意味)のままにします。 Modelerの決定木のモデル作成ノードはカテゴリ変数を数値型にコード化しなくてもそのままつかえますので、これで設定は終了です。 1p.①説明変数、目的変数の定義 scikit-learn版 PythonのScikit-LearnのDecisionTreeClassifierをつかって決定木のモデルを作成します。Sckit-LearnのDecisionTreeClassifierはカテゴリ変数を数値型のコード化しないと受け付けてくれず、「ValueError: could not convert string to float: 'More than 2'」のエラーになります。 そのため、カテゴリ変数の数値コード化をまず行う必要があります。 また、目的変数と説明変数は別の配列やDataframeに分ける必要があります。 まず目的変数の数値コード化し、配列を作ります。 目的変数のエンコード from sklearn.preprocessing import LabelEncoder #目的変数のエンコード le = LabelEncoder() y = le.fit_transform(df['Credit_rating'].values) print(le.classes_) print(type(df['Credit_rating'].values)) print(type(y)) print(y) 数値コード化を行うLabelEncoderをインスタンス化し、fit_transformでnumpy配列を得ます。 「Bad」と「Good」というカテゴリ値が0と1に変換されています。 結果 ['Bad' 'Good'] <class 'numpy.ndarray'> <class 'numpy.ndarray'> [0 0 0 ... 1 1 1] 次に説明変数をコード化します。 説明変数のエンコード import category_encoders as ce #説明変数のエンコード ce_oe = ce.OrdinalEncoder(cols=['Income','Credit_cards', 'Education', 'Car_loans'], handle_unknown='impute') X = ce_oe.fit_transform(df)[['Age','Income', 'Credit_cards', 'Education', 'Car_loans']] X.head() ここではcategory_encodersのOrdinalEncoderをつかってカテゴリ変数の説明変数を数値化しました。もともと数値型で変換の不要なAgeを除いて、cols=['Income','Credit_cards', 'Education', 'Car_loans']を指定しています。 XにはAgeも含む説明変数のみのDataframeを入力しています。 'Income','Credit_cards', 'Education', 'Car_loans'が1や2という整数に置き換わっているのがわかります。 どのようにmappingが行われたのかはce_oe.mappingをみるとわかります。 例えば、Credit_cardsの「5 or more」は1、「Less than 5」は2にマッピングされていることがわかります。 しかし、Incomeのマッピングはイマイチよくありません。 Medium: 1 Low: 2 High: 3 とマッピングされていますので、Medium<Low<Highの順序なのです。このままでもモデルは作れますが、決定木がとても解釈しにくくなるので、Low<Medium<Highの順序に修正します。 目的変数のエンコード #IncomeのエンコードがMedium<Low<Highの順序なのでLow<Medium<Highの順序に修正 ce_oe.mapping[0]['mapping']={None: 0, 'Low': 1, 'Medium': 2, 'High': 3} pprint.pprint(ce_oe.mapping) Incomeのマッピングが修正されました。 指定しなおしたIncomeのマッピングの記述方法が、自動生成されたものと異なりますが、以下のマニュアルを参考に設定しました。 修正したマッピングで変換しなおします。 X = ce_oe.fit_transform(df)[['Age','Income', 'Credit_cards', 'Education', 'Car_loans']] X.head() これで説明変数と目的変数の準備が終わりました。 Modelerと比較するとかなりややこしい処理が必要でした。 2m.②モデル作成 Modeler版 ModelerにはC&RTree(CART)、CHAID、C5.0、QUESTという4つの決定木のアルゴリズムが実装されています。一方ScikitLearnのDecisionTreeClassifierではCARTしか実装されていませんので、ここではCARTのモデル作成を行います。 C&R Treeのモデル作成ノードを接続します。そのまま実行してもモデルは作成できるのですが、ここではDecisionTreeClassifierとなるべく似た設定にします。オプションについての詳しい内容は4. オプション設定の違いにまとめました。 まず、「作成オプション」タブ「基本」項目の「過剰適合を回避するためにツリーを剪定」のチェックを外します。 もうひとつ、詳細設定項目の「オーバーフィット防止設定」を0にし、実行します。 モデルができたら、「編集」で中身を確認します。 「ビューア―」タブで「度数情報を表とグラフで表示」ボタンを押すと以下のようにツリーができます。 Incomeが「Medium」か「High」でCredit_cardsが「Less than 5」だとGoodが91.001%になっていることがわかります。 2p.②モデル作成 Sckit-Learn版 Sckit-LearnのCARTモデルはDecisionTreeClassifierで作ります。 max_depthなどのパラメーターはModelerのデフォルト値に近いもので指定しました。 fit(X, y)で説明変数と目的変数を与えてモデルを作成します。 モデル作成 #Cartモデル作成のパッケージ from sklearn.tree import DecisionTreeClassifier #モデル作成 clf = DecisionTreeClassifier(max_depth=5,min_samples_split=0.02,min_samples_leaf=0.01,min_impurity_decrease=0.0001) clf = clf.fit(X, y) 可視化にはgraphvizを使っています。graphvizの導入については以下を参考にして導入しました。 Download | Graphviz graphvizがインポートできない - Qiita 以下のコードで可視化します。 export_graphvizでモデルを指定して、dot_dataとして出力しています。 説明変数をfeature_names=['Age','Income', 'Credit_cards', 'Education', 'Car_loans']で指定します。 目的変数のラベルをle.classes_(中身は['Bad', 'Good'])で指定します。 モデルの可視化 #下記は決定木可視化のためのツール from sklearn.tree import export_graphviz import graphviz import pydotplus from IPython.display import Image from six import StringIO #決定木表示 dot_data = StringIO() #dotファイル情報の格納先 export_graphviz(clf, out_file=dot_data, feature_names=['Age','Income', 'Credit_cards', 'Education', 'Car_loans'], class_names=le.classes_, filled=True, rounded=True, special_characters=True) graph = pydotplus.graph_from_dot_data(dot_data.getvalue()) Image(graph.create_png()) 以下のような決定木のグラフが表示されます。Modelerでみた同じノードをたどると、 NOT Income<=1.5なので、IncomeがMedium(2),High(3)であり、 NOT Credit_cards<=1.5なので、Credit_cardsが「Less than 5」(2)の場合に648/712=91%がGoodだということがわかります。 Modelerの決定木のグラフとDecisionTreeClassifierの決定木のグラフの表示内容の対応を示しました。 まず、Not Credit_cards<=1.5なので2が含まれると判定し、Credit_cards=2は「Less than 5」を意味するというように、カテゴリ値を数値コード化してから、解釈しなければならないので、Modelerの決定木にくらべて圧倒的に読みづらいです。 また、パーセンテージも計算が必要ですし、棒グラフの表示がないため、直感的にどのノードや葉が重要なのかがわかりにくいグラフです。ただ色分けはされていて、この例だとGoodでは青、Badがオレンジになっています。また、gini係数が小さい(つまりBadとGoodの差が大きい)と色が濃くなるようになってはいます。 やはり読みやすさには大きな差があります。DecisionTreeClassifierでは次の分岐の条件がノードの上に来ているのも直感的ではなく分かりにくいと思います。 決定木は、データサイエンティストやITの専門家ではない現場の方にも理解可能なグラフですので、特に読みやすさは重要になります。 3m.③スコアリング Modeler版 Modelerでのスコアリングはモデルナゲットにテーブルノードをつなげて実行するだけです。 2つ追加された列にスコアリング結果がはいります。\$Credit_ratingに予測結果、\$RC-Credit_ratingに予測の確信度が入ります。 3p.③スコアリング Sckit-Learn版 Sckit-learnでのスコアリングは.predict(説明変数のdataframe)で行い、結果はnumpyの配列で戻ります。また、この結果は数値コード化後の値です。 print(clf.predict(X)) print(type(clf.predict(X))) 結果 [0 0 0 ... 1 1 1] <class 'numpy.ndarray'> Modelerの結果のように元のDataFrameに列で追加する場合は以下のようなコードになります。 le.inverse_transformで数値コードをカテゴリ値に戻しています。 スコアリング df_pred = df.copy() #予測結果を追加 df_pred['$Credit_rating']=le.inverse_transform(clf.predict(X)) また確信度は.predict_proba(説明変数のdataframe)で行い、結果はやはりnumpyの配列が2次元で戻ります。2次元なのはBadとGoodでそれぞれの確信度を戻すためです。 print(clf.predict_proba(X)) print(type(clf.predict_proba(X))) 結果 [[0.50129199 0.49870801] [0.87719298 0.12280702] [0.92753623 0.07246377] ... [0.41304348 0.58695652] [0.21296296 0.78703704] [0.12 0.88 ]] <class 'numpy.ndarray'> Modelerの結果のように元のDataFrameに列で追加する場合は以下のようなコードになります。max(axis=1)を行うことでBadとGoodの大きい方を返すようにしています。 スコアリング確信度 #予測結果の確信度を追加 df_pred['$RC-Credit_rating']=clf.predict_proba(X).max(axis=1) 4. オプション設定の違い ModelerとDecisionTreeClassifierのCARTのツリーを作る際のオプションを比較します。どちらかにしかない設定(NA項目)も多くありました。Modelerには「過剰適合を回避するためにツリーを剪定」や「オーバーフィット防止セット%」などのオーバーフィットを避けるためのオプションがより豊富です。 またデフォルト値についても、Modelerはオーバーフィットをさけるようなデフォルト値がとられているので、とりあえずデフォルトでも過学習しすぎないモデルができます。 一方でDecisionTreeClassifierはmax_depth=NONE,min_samples_split=2,min_samples_leaf=1という、最後の一件まで分割するような設定がデフォルトになっていて、ほぼ間違いなく過学習します。せめて今回設定を行ったような、max_depth=5,min_samples_split=0.02,min_samples_leaf=0.01くらいの設定はしないと使い物にならないと思います。 Modeler デフォルト 今回の設定 DecisionTreeClassifier デフォルト 今回の設定 最大ツリーの深さ 5 max_depth None 5 過剰適合を回避するためにツリーを剪定 オン オフ NA リスク (標準誤差) の最大差を設定 オフ NA 最大代理変数 5 NA 停止規則パーセンテージを使用親枝葉 2 min_samples_split オフ 0.02 停止規則パーセンテージを使用子枝葉 1 min_samples_leaf オフ 0.01 停止規則絶対値を使用親枝葉 オフ min_samples_split 2 停止規則絶対値を使用子枝葉 オフ min_samples_leaf 1 不純度の最小変化 0.0001 min_impurity_decrease 0 0.0001 カテゴリー対象の不純度の測度 Gini criterion gini オーバーフィット防止セット% 30 0 NA NA max_leaf_nodes None なお、完全に同じ設定にすることはできず、今回のツリーも細かいところは異なっていて、DecisionTreeClassifierの方がより葉を伸ばしたツリーになっていました。 5. サンプル サンプルは以下に置きました。 ストリーム https://github.com/hkwd/200611Modeler2Python/blob/mastercarttree/carttree2.str?raw=true notebook https://github.com/hkwd/200611Modeler2Python/blob/master/carttree/carttree3.ipynb データ https://raw.githubusercontent.com/hkwd/200611Modeler2Python/master/data/tree_credit.csv ■テスト環境 Modeler 18.3 Windows 10 64bit Python 3.8.10 pandas 1.0.5 sklearn 0.23.2 category_encoders 2.2.2 graphviz 0.17 6. 参考情報 ディシジョン・ツリー・ノード - 基本 - IBM Documentation 【機械学習】決定木をscikit-learnと数学の両方から理解する - Qiita sklearn.tree.DecisionTreeClassifier — scikit-learn 0.24.2 documentation Ordinal — Category Encoders 2.2.2 documentation
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python]FastAPIのチュートリアルをやってみた

タイトルの通りですが、FastAPIの公式ドキュメントにチュートリアルがあったので、一通りやってみました。 私自身は最近プライベート(Pythonチュートリアルをやってみた)や業務でPythonを触り始めたばかりで、Pythonのフレームワークを扱ったことはありません。 Pythonのフレームワークといえば、DjangoやFlaskというイメージでしたが、「自動ドキュメント生成機能がすごいらしい」と聞いて気になっていたので、FastAPIを触ってみることにしました。 FastAPIとは Python 製 Web フレームワークを Flask から FastAPI に変えた話 [FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する 私も詳しくないのでこちらのふたつの記事を参考にさせていただいたのですが、Flaskに似たマイクロフレームワークで、Pythonで最近サポートされた機能(型ヒントなど)を生かしてOpenAPI(Swagger)仕様のAPIドキュメントを自動で生成する機能を持っています(APIを実装するだけで下記のドキュメントが自動で生成され、ブラウザでアクセスできるように) また、ドキュメントが充実していて、チュートリアルとAdvanced User Guideの目次を一覧するだけでも、APIとして必要な機能が網羅されているのがわかります。 さらに、APIの書き方も直感的で、公式の代替ツールから受けたインスピレーションと比較によるとPythonの標準モジュールであるRequestsから影響を受けているとのことです。 使い方はとても簡単です。例えば、GETリクエストを実行するには、このように書けば良いです: response = requests.get("http://example.com/some/url") 対応するFastAPIのパスオペレーションはこのようになります: @app.get("/some/url") def read_url(): return {"message": "Hello World"} 他にも、Pydanticという型バリデーションライブラリを含んでいて、リクエスト・レスポンスのバリデーションを標準で効かせられたり、StarletteというフレームワークのTestClientを使うことで、テストの中で簡単にリクエストを再現できます。 from fastapi import FastAPI from fastapi.testclient import TestClient app = FastAPI() @app.get("/") async def read_main(): return {"msg": "Hello World"} client = TestClient(app) def test_read_main(): response = client.get("/") assert response.status_code == 200 assert response.json() == {"msg": "Hello World"} チュートリアルをやってみた感想 所要時間は20時間でした。私はPython自体初学者で、本なども読むのが遅い方なので時間がかかった方だと思います。 まずチュートリアルの説明がめちゃくちゃ丁寧です。 チャプターごとにサンプルコードに記載してある内容を一つ一つ説明してくれますし、Pythonの基本用語まで備考として説明されていました。 また、認証に関してはトークンの扱いなど、APIを構築する上で必要な知識を含めて説明されていました。 ドキュメントが充実していて、公式サイトも見やすく、また、FastAPI自体が直感的にAPIを記述できるようになっているので、軽量で扱いやすいフレームワークであるという印象を受けました。 ただ、チュートリアルの半分くらいが日本語の翻訳がされておらず英語のままなので、Dependenciesなどフレームワーク独自の考え方の部分は、さすがに理解度が落ちました? 翻訳に参加もできるようです (気になるなぁ。。。) なんか作ってみようかな!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

matplotlibの2DヒストグラムでX軸、Y軸、Z軸をログ表示する方法

使い所 matplotlib の 2Dヒストグラムで、全軸(X, Y, Z) をログ表示する方法を紹介する。 (横縦軸が log-log の 2dhist で zscale も log の図という意味です。) pcolormesh を使う方法 # -*- coding: utf-8 -*- """2dhist_log_log_log.ipynb Original file is located at https://colab.research.google.com/drive/1VGo0WdmmUfxqnLdrWVJ98ZO4rJj0oX58 xscale log, yscale log, zscale log, for 2dhist using pcolormesh """ import numpy as np from matplotlib.colors import LogNorm import matplotlib.cm as cm import matplotlib.pyplot as plt # create sample data x, y = np.random.exponential(size=(2, 100000)) x = x + 1 y = y * 2 plt.figure(figsize =(10, 7)) # create uniform x, y range in a log space. xmin = np.log10(x.min()) xmax = np.log10(x.max()) ymin = np.log10(y.min()) ymax = np.log10(y.max()) print(xmin, xmax, ymin, ymax) xbins = np.logspace(xmin, xmax, 50) # <- make a range from 10**xmin to 10**xmax ybins = np.logspace(ymin, ymax, 50) # <- make a range from 10**ymin to 10**ymax # create 2D hist using numpy.histogram2d counts, _, _ = np.histogram2d(x, y, bins=(xbins, ybins)) # plot 2d hist pcm = plt.pcolormesh(xbins, ybins, counts, norm=LogNorm(vmin=1, vmax=counts.max()), cmap='PuBu_r') plt.colorbar(pcm) plt.xscale('log') plt.yscale('log') plt.xlim(xmin=xbins[0]) plt.xlim(xmax=xbins[-1]) plt.ylim(ymin=ybins[0]) plt.ylim(ymax=ybins[-1]) 生成される図がこちら。 コードは、google Colab 上にもあります。 注意点 エラーがでないのでわかりにくいのが、plt.pcolormesh を用いるか、ax.pcolormesh を用いるか、設定が不適切な適切な場合、エラーなく表示されてしまうのであるが、log にならなかったり、表示範囲が設定されないことがある。(ax の使い方が適切であれば動くのかもしれない。。)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【機械学習】「橋本環奈ちゃん画像判別アプリ」を実装してみる

はじめに フリーランスとしてスマホ(iOS - Swift/Objective-C)アプリ/Webアプリ(Python2x/3x)開発を12年ほどやっていますJinanbouと申します。 昨今話題の「機械学習」に対し、アプリ開発経験者がどこまでモノに出来るのか挑戦したく始めてみました。 初めは独学でネット上の情報を元にPyTorchやTensorflowのサンプルコードを動かしたりしていましたが、基本的なことが理解できていないため、サンプルコードから発想をもって拡張していくことが全く出来ず、 ・Tensorflowやkeras、scikit-learnといったツールの使い方だけでなく、機械学習に必要な基礎知識やアプローチの仕方 を学ぶべく、Aidemyのプレミアムプラン( https://aidemy.net/grit/premium/ )3ヶ月で学習を行いました。 本記事は、Aidemyの卒業時の提出用アプリケーションとして実装したものとなり、3ヶ月程度でこれくらいの知識が得られたことの軌跡として残したいと思いました。 本記事の概要 ・この記事では、橋本環奈ちゃんの顔画像(211枚)とそれ以外の人物(LFW - http://vis-www.cs.umass.edu/lfw/ - が公開している顔認識ベンチマーク・検証用顔画像の中から取り出した男女50%ほどずつの1000枚)の顔画像を学習させ「確率が高いものを橋本環奈ちゃんとして判別する」アプリケーションの構築の流れを記載しています。 ・コードの実行環境はAnacondaで作成したPython3.8.2環境上となり、すべてJupyter-labsで行っています。 構築の流れ 本アプリの実装はざっくりと以下の流れで行いました。 1. 正解データ(ラベル:0)とする「橋本環奈ちゃん」の画像の取得 2. 1.で取得した橋本環奈ちゃんの画像から「顔部分」だけを切り出し 3. 不正解データ(ラベル:1)とする「橋本環奈ちゃん以外の人の顔画像」=>LFWの画像の取得 4. 2.と3.の画像を元に、tensorflow.kerasの学習モデルで取り扱いしやすくするためにnumpy配列に変換/保存する(.npyファイルとして保存) 5. 4.で作成したnumpy配列データを使用してkerasで学習を行い、モデルファイル(.h5)を保存する 6. 保存したモデルファイル(.h5)をFlaskのWebアプリに組み込む 1. 正解データ(ラベル:0)とする「橋本環奈ちゃん」の画像の取得 まずはじめに、今回判別したいものとして「橋本環奈ちゃん」の画像を取得します。 取得はGoogleの画像検索を使いました。 Aidemyでの学習ではXMLやHTMLのパースを行う「BeautifulSoup」を使用しましたが、動的に検索結果がレンダリングされるGoogle画像検索ではSeleniumの方がやりやすいかなと思い、SeleniumのwebdriverでChromeをオートメーション化して、 ・画像サムネイルのクラス「rg_i」を探査してクリックをさせ、画面右側に表示されるオリジナル画像のURLを取得/あとでまとめてそれらの画像をダウンロードする というアプローチで画像を取得しました。 Jupyter-labsの実行ファイルと同じディレクトリに「images」フォルダを作成してから実行することで画像がダウンロードされます。 以下がそのコードです。 Jupyter-labsで実行可能 from selenium import webdriver import time import datetime import os import requests import hashlib from os import listdir from os.path import join #webブラウザを起動 browser = webdriver.Chrome() query = '橋本環奈' browser.get('https://www.google.co.jp/search?q=%s&tbm=isch' % query) time.sleep(10) image_urls = set() for thumbnail in browser.find_elements_by_css_selector('img.rg_i'): thumbnail.click() time.sleep(1) for img in browser.find_elements_by_css_selector('img.n3VNCb'): image_urls.add(img.get_attribute('src')) print("image_urls: {0}".format(image_urls)) index = 0 failed = [] for image_url in image_urls: if image_url == None: continue try: response = requests.get(image_url, stream=True) if response.status_code == 200: contenttype = response.headers['content-type'] if contenttype == None: extension = '.bin' if contenttype.find('jpeg') != -1: extension = '.jpg' elif contenttype.find('png') != -1: extension = '.png' elif contenttype.find('gif') != -1: extension = '.gif' else: extension = '.bin' filename = 'images/%04d%s' % (index, extension) index += 1 with open(filename, 'wb') as file: for chunk in response.iter_content(chunk_size=1024): file.write(chunk) print('Downloaded: %s' % image_url) except: print('Failed: %s' % image_url) failed.append(image_url) def md5(fname): hash_md5 = hashlib.md5() with open(fname, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): hash_md5.update(chunk) return hash_md5.hexdigest() hashes = set() for file in listdir('./images'): file = join('images', file) h = md5(file) if h in hashes: print('Duplicated image: %s' % file) continue hashes.add(h) 2. 1.で取得した橋本環奈ちゃんの画像から「顔部分」だけを切り出し 次に、上記1.でダウンロードした橋本環奈ちゃんの画像から「顔部分だけ」を切り出しします。 切り出しのための顔認識はOpenCVのhaarcascade_frontalface_default.xmlを使用しました。 Jupyter-labsの実行ファイルと同じディレクトリに「org」「png」「png_resize」「face」フォルダを作成し、「org」フォルダに上記1.で取得した橋本環奈ちゃんの画像をすべて入れてから実行することで顔部分の画像だけが「face」フォルダに保存されます。 また「org」フォルダに設置した画像が.jpg形式ではない場合に.jpg形式に変換してから処理を行います。 以下がそのコードです。 Jupyter-labsで実行可能 import os import subprocess from PIL import Image import cv2 as cv dir0 = 'org' dir1 = 'png' dir2 = 'png_resize' dir3 = 'face' files0 = os.listdir(dir0) files0.sort() for file in files0: if '.jpg' in file: #command = 'sips --setProperty format png ' + dir0 +'/' + file + ' --out ' + dir1 +'/' + file.replace('.jpg','.png') #jpg to png command = 'sips --setProperty format jpeg ' + dir0 +'/' + file + ' --out ' + dir1 +'/' subprocess.call(command, shell=True) #print(file) files1 = os.listdir(dir1) files1.sort() print("length of files1: {0}".format(len(files1))) if len(files1)==0: print("orgにjpg画像をセットしてください") # aaa.jpg print("----") for file in files1: if '.jpg' in file: img0 = os.path.join(dir1, file) img0_img = Image.open(img0) h = img0_img.height w = img0_img.width img1_img = img0_img.resize((600,round(600*h/w))) img1 = os.path.join(dir2, file) img1_img.save(img1) #print(file) # aaa.png files2 = os.listdir(dir2) files2.sort() print("length of files2: {0}".format(len(files2))) face_cascade = cv.CascadeClassifier('haarcascade_frontalface_default.xml') margin = 20 #切り出し範囲を拡張する for file in files2: if '.jpg' in file: dirfile = os.path.join(dir2, file) img = cv.imread(dirfile) gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) print("あるよ") faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x,y,w,h) in faces: print("margin: "+str(margin)) face = img[y-margin:y+h+margin, x-margin:x+w+margin] face_name = str(file.strip('.jpg'))+'_'+str(x)+'_'+str(y)+'.jpg' dirface = os.path.join(dir3,face_name) print("dirface: "+dirface) print("face_name: "+face_name) try: facefile = cv.imwrite(dirface, face) #cv.rectangle(img,(x-10,y-10),(x+w+10,y+h+10),(255,0,0),2) print(face_name) except: pass 3. 不正解データ(ラベル:1)とする「橋本環奈ちゃん以外の人の顔画像」=>LFWの画像の取得 橋本環奈ちゃん以外の人間の顔画像をある程度学習させておく必要があるため、LFW( http://vis-www.cs.umass.edu/lfw/ )が公開している顔認識ベンチマーク・検証用顔画像の中から取り出した男女50%ほどずつの1000枚の画像を使用します。 4. 2.と3.の画像を元に、tensorflow.kerasの学習モデルで取り扱いしやすくするためにnumpy配列に変換/保存する(.npyファイルとして保存) 正解データと不正解データ、それぞれが画像ファイル(.jpg)となっているわけだが、この画像ファイルのままではkerasでの学習モデル構築における学習や検証などの計算量が膨大になってしまうので、あらかじめ扱いやすい形式となる「Numpy行列データ(.npy形式)」に変換・保存を行う。 以下がそのコードです。 Jupyter-labsで実行可能 ## 「kanna」フォルダ=>正解データ #「other」フォルダ=>不正解データ #として、フォルダ内に入っている画像を元に、 # 自分でトレーニング用とテスト用のファイルを分割するコード(2021年09月15日09:41) from PIL import Image import os,glob import numpy as np #from sklearn import cross_validation from sklearn import model_selection import datetime classes = ["kanna","other"] num_classes = len(classes) image_size = 150 X_train = [] X_test = [] Y_train = [] Y_test = [] for index,classlabel in enumerate(classes): photos_dir = "./data/"+classlabel files = glob.glob(photos_dir + "/*.jpg") for i, file in enumerate(files): #print("i: {0}".format(i)) #if i >= 200:break image = Image.open(file) image = image.convert("RGB") image = image.resize((image_size,image_size)) data = np.asarray(image) if classlabel=="kanna": if i < 60: #テスト用に60件のデータをX_testとY_testに入れておき、残りは学習用にX_train,Y_trainに入れる X_test.append(data) Y_test.append(index) else: X_train.append(data) Y_train.append(index) else: if i < 300: #テスト用に300件のデータをX_testとY_testに入れておき、残りは学習用にX_train,Y_trainに入れる X_test.append(data) Y_test.append(index) else: X_train.append(data) Y_train.append(index) X_train = np.array(X_train) X_test = np.array(X_test) y_train = np.array(Y_train) y_test = np.array(Y_test) xy = (X_train,X_test,y_train,y_test) np.save("./data_202109141744.npy",xy) print("完了です。完了日時: "+str(datetime.datetime.now())) 5. 4で作成したnumpy配列データを使用してkerasで学習を行い、モデルファイル(.h5)を保存する いよいよtensorflow.kerasにて学習モデルの構築を行います。 以下がそのコードです。 Jupyter-labsで実行可能 # ここからはanimal_cnn.pyの内容 from tensorflow.keras.models import Model, Sequential,load_model from tensorflow.keras.layers import Dense, Dropout, Flatten, Input,Conv2D, MaxPooling2D,Activation,BatchNormalization from tensorflow.keras.utils import to_categorical from tensorflow.keras import optimizers,regularizers from tensorflow.keras.callbacks import EarlyStopping import random import matplotlib.pyplot as plt from tensorflow.keras.applications.vgg16 import VGG16 import cv2 import re import os import numpy as np from PIL import Image classes = ["kanna","other"] num_classes = len(classes) image_size = 150 # メインの関数を定義する def execute(): #「kanna」に入れた正解の正方形画像(橋本環奈ちゃんの顔画像)と「other」に入れた不正解画像をnumpy形式にしたものをロードする。 #X_train, X_test, y_train, y_test = np.load("./kanna.npy",allow_pickle=True) #フォルダ内の画像だけで作成したnumpyのデータ X_train, X_test, y_train, y_test = np.load("./data_202109141744.npy",allow_pickle=True) #フォルダ内の画像を元に「1.回転(傾き)」「2.左右反転」してデータを水増ししたnumpyのデータ print("X_train.shape: {0}".format(X_train.shape)) print( "X_test.shape: {0}".format(X_test.shape)) print("y_train.shape: {0}".format(y_train.shape)) print(" y_test.shape: {0}".format(y_test.shape)) batch_size = len(X_train[0][0]) print("batch_size: {0}".format(batch_size)) #return X_train = X_train.astype("float") / 256 #整数を浮動小数点に変換する X_test = X_test.astype("float") / 256 y_train = to_categorical(y_train,num_classes) y_test = to_categorical(y_test, num_classes) model,history = model_train(X_train, y_train, X_test, y_test) ###### 学習過程をグラフで描画する - ここから metrics = ['loss', 'accuracy'] # 使用する評価関数を指定 plt.figure(figsize=(20, 7)) # グラフを表示するスペースを用意 for i in range(len(metrics)): metric = metrics[i] # historyから値を取り出すためのキーとして使用 plt.subplot(1, 2, i+1) # figureを1×2のスペースに分け、i+1番目のスペースを使う plt.title(metric) # グラフのタイトルを表示 plt_train = history.history[metric] # historyから訓練データの評価を取り出す plt_test = history.history['val_' + metric] # historyからテストデータの評価を取り出す plt.plot(plt_train, label='training') # 訓練データの評価をグラフにプロット plt.plot(plt_test, label='test') # テストデータの評価をグラフにプロット plt.legend() # ラベルの表示 ###### 学習過程をグラフで描画する - ここまで model_eval(model, X_test,y_test) def model_train(X,y,x_t,y_t): #畳み込みの定義を行う model = Sequential() model.add(Conv2D(32,(3,3),padding="same",input_shape=X.shape[1:])) model.add(Activation('relu'))#1層目 model.add(Conv2D(32,(3,3))) #2層目 model.add(Activation('relu'))#3層目 model.add(MaxPooling2D(pool_size=(2,2))) model.add(Dropout(0.25)) model.add(Conv2D(64,(3,3),padding='same')) model.add(Activation('relu')) model.add(Conv2D(64,(3,3))) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=(2,2))) model.add(Dropout(0.25)) model.add(BatchNormalization()) model.add(Flatten()) #データを1列に並べるFlatten model.add(Dense(512)) #データを全結合する model.add(Activation('relu')) model.add(Dropout(0.5))#データを半分捨てる model.add(Dense(2))#最後の出力層のノードは1とする model.add(Activation('softmax')) #学習が進まなくなったら停止させるためのコールバック関数 es_cb = EarlyStopping(monitor="val_loss", patience=10, verbose=1, mode='auto') #過学習防止のため、val_lossが10回連続で上がったら学習を止める。 #history = model.fit(X, y, batch_size=32, epochs=200, validation_data=(x_t, y_t),callbacks=[es_cb]) batch_size = len(X[0][0][0]) #最適化を行う model.compile(loss='categorical_crossentropy', optimizer=optimizers.SGD(learning_rate=0.000008,decay=0.001, momentum=0.9), metrics=['accuracy']) #学習開始 history = model.fit(X,y, batch_size=150, epochs=700, #steps_per_epoch= validation_data=(x_t, y_t),callbacks=[es_cb]) model.summary() model.save('./kanna_detector_lr0-000008_dc0-00005_bs150_202109141610.h5') #モデルの保存 return model,history def model_eval(model, X, y): scores = model.evaluate(X, y, verbose=1) print("Test Loss: ", scores[0]) print("Test Accuracy: ", scores[1]) execute() 学習結果は以下のようなグラフとなっています。(過学習しています。。。) acc: 95.83 % val_loss: 10.52% 6. 保存したモデルファイル(.h5)をFlaskのWebアプリに組み込む 以下がHerokuにアップしたFlask製のアプリです。 Flaskがインストールされている環境で動作可能 import os from flask import Flask, request, redirect, render_template, flash from werkzeug.utils import secure_filename from tensorflow.keras.models import Sequential, load_model from tensorflow.keras.preprocessing import image from PIL import Image, ImageOps import numpy as np import logging import cv2 from tensorflow.keras.applications.inception_v3 import InceptionV3, preprocess_input IMAGE_SIZE = 150 UPLOAD_FOLDER = "/var/www/html/targetdetector_20210916/static/uploads" ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif']) app = Flask(__name__,static_folder="static") app.logger.debug('DEBUG') app.logger.info('INFO') app.logger.warning('WARNING') app.logger.error('ERROR') app.logger.critical('CRITICAL') #model = load_model('./model_20210706_1.h5')#学習済みモデルをロード #model = load_model('./people_detector_lr0-000008_dc0-00001_b150_202109122316.h5')#裕也の顔画像を判定する学習済みモデルをロード model = load_model('/var/www/html/targetdetector_20210916/kanna_detector_lr0-000008_dc0-001_bs150_202109150931.h5')#橋本環奈の顔画像を判定する学習済みモデルをロード def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS #アップロードされた画像ファイルから顔部分を抜き出して独自モデルで判別処理を行う def predict(filepath): face_found_flg = 0 face_cascade = cv2.CascadeClassifier('/var/www/html/targetdetector_20210916/haarcascade_frontalface_default.xml') img = cv2.imread(filepath) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, minSize=(20,20)) print("faces: "+str(faces)) print("filepath: "+filepath) margin = 10 #切り出し範囲を拡張する filename = filepath.replace('/var/www/html/targetdetector_20210916/static/uploads/','') for (x,y,w,h) in faces: print("margin: "+str(margin)) face = img[y-margin:y+h+margin, x-margin:x+w+margin] dirface = os.path.join("/var/www/html/targetdetector_20210916/static/face",filename) print("dirface: "+dirface) print("filename: "+filename) try: facefile = cv2.imwrite(dirface, face) #cv2.rectangle(img,(x-10,y-10),(x+w+10,y+h+10),(255,0,0),2) face_found_flg = 1 filepath = dirface except: pass if face_found_flg==1: img = image.load_img(filepath, target_size=(IMAGE_SIZE,IMAGE_SIZE)) img = img.convert('RGB') #画像データを64 x 64に変換 _image = img.resize((IMAGE_SIZE, IMAGE_SIZE)) _image.save("/var/www/html/targetdetector_20210916/test.png") # 画像データをnumpy配列に変換 img = np.asarray(_image) img = img / 255.0 result = model.predict(np.array([img]),verbose=1) print('result:',result) predicted = result.argmax() print('predicted:',predicted) ok_count,ng_count = 0,0 if predicted==1: ng_count = 1 else: if result[predicted][0]>0.69: ok_count = 1 else: ng_count = 1 print("ok_count: {0}".format(ok_count)) print("ng_count: {0}".format(ng_count)) if ok_count>ng_count: pred_answer = "【正解】学習済みの画像です!" else: pred_answer = "【不正解】学習済みの画像ではありません!" else: pred_answer = "【顔検出エラー】顔部分が適切に検出出来ませんでした。" return pred_answer,filepath @app.route('/', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': if 'file' not in request.files: flash('ファイルがありません') return redirect(request.url) file = request.files['file'] if file.filename == '': flash('ファイルがありません') return redirect(request.url) if file and allowed_file(file.filename): filename = secure_filename(file.filename) print('filename: '+filename) file.save(os.path.join(UPLOAD_FOLDER, filename)) filepath = os.path.join(UPLOAD_FOLDER, filename) #filepath = filepath.replace('/var/www/html/targetdetector_20210916/','/') print("filepath: {0}".format(filepath)) #受け取った画像を読み込み、np形式に変換 pred_answer,filepath = predict(filepath) print('pred_answer:',pred_answer) print('filepath:',filepath) filepath = filepath.replace('/var/www/html/targetdetector_20210916','') items = {"filepath":filepath} return render_template("index.html",answer=pred_answer,items=items) return render_template("index.html",answer="",items=None) if __name__ == "__main__": port = int(os.environ.get('PORT', 5000)) app.run(host ='0.0.0.0',port = port) 出来上がったアプリ 以下のURLのものが出来上がったアプリとなります。 橋本環奈ちゃんの画像をアップしてみてください。画像の中から顔部分だけを抽出して、その顔画像を判定します。 機械学習をやってみての振り返り Aidemyでの学習 独学の後、Aidemyのプレミアムプラン( https://aidemy.net/grit/premium/ )で学んでみたわけだが、 体系的に機械学習を学べたのはとても良かったと思う。 また、機械学習を行う上で必要なツール(Google Colab、Jupyter-notebook)やライブラリ群(Numpy、Pandasなど)において、そもそも一般的には何を使うと効率的に作業が出来るか、細かいところだけどこういう「その道に居る人達の間では当たり前のこと」を知ることが出来たのは、個人的に機械学習を発展させていくために大いに役立った。 一つ希望を言わせてもらえば、もうちょっと実践(コード演習よりももっと「テーマを決めて完成させる」系のもの)があったら良かったなぁと思う。 最後に残った問題点 これはやっぱり「精度を上げるためにはどうしたらよいかが明確に分からない」ということ。 今回実装した上記のアプリについても、(グラフを見てもらえるとわかると思うが過学習しているわけだが)accが95.83% val_lossが10.52%と、数値的に見るとそこそこ正解判定が正しくできそうなわけだが、 ・学習させた画像で試しても正解にならないことがある ・他の人(女性)の画像をで試すと正解になってしまうことがある。 という、体感的な正答率が数値と結構かけ離れているなぁと感じており、このギャップを埋めるために、 if result[predicted][0]>0.69: ok_count = 1 else: ng_count = 1 といった形で、学習モデルによる判定結果の値から「正解率が69%以上であれば橋本環奈ちゃんとする!」といった処理を入れているわけだが、こういうやり方が適切なのかどうかがはっきりいって分からない。 こうした「実際にアプリケーションとして組み込む上でのノウハウ」などは続けていく中で見えてくるものだとは思うので、実用性を目指して繰り返し学んでいこうと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む