20220113のPythonに関する記事は28件です。

ニュース記事の感情分析(ネガポジ判定ないし極性判定とも)

はじめに 過去の記事(※1)で自然言語処理をする機会が増えてますとお伝えしてました。 いろいろと手を出してて、感情分析(ネガポジ判定ないし極性判定とも)にもチャレンジしてもいたり。。。 この2~3か月くらい、筆者は仕事でデータ収集(BeautifulSoupやSeleniumを使ったWebスクレイピング)と自然言語処理(MeCabを使った形態素解析とgensim使った単語の分散表現と感情分析)をする機会が増えてます。 本稿では、「livedoor NEWSに対して感情分析をする」過程をCode交えて紹介してゆく。 感情分析とは 感情分析とは、とある文章に込めた想い(感情)がポジティブかネガティブかを、文章に含まれる単語軍から推定(分類)するタスクです。 東北大学でも関連研究に取り組まれており、資材(日本語評価極性辞書12)も公開されております。 本稿で利用するレポジトリはこちらです。 また、ハンズオンはこちらです。 本稿で紹介すること 使った環境 感情分析の進め方(ニュース記事を例に) 本稿で紹介しないこと BERTの全般 ニュース記事のスクレイピング (筆者の過去投稿を参照いただけると。。。) 2022年1月1日の話題のニュース記事を予め取得しCSVファイルに出力して利用します。 まずは、環境の準備 筆者は、NGCカタログイメージを取得し、コンテナ起動しています。 以下、NGCカタログイメージ3の取得コマンドです。 $ docker pull nvcr.io/nvidia/pytorch:20.11-py3 以下、PythonとPythonライブラリ群のVer情報です。 $ python -V Python 3.6.10 :: Anaconda, Inc. $ python -m pip --version pip 20.2.4 from /opt/conda/lib/python3.6/site-packages/pip (python 3.6) $ pip list | grep -i torch pytorch-transformers 1.1.0 torch 1.8.0a0+17f8c32 torchtext 0.8.0a0 torchvision 0.8.0a0 $ pip list | grep -i transformers pytorch-transformers 1.1.0 transformers 3.0.2 $ mecab -v mecab of 0.996 それでは、ニュース記事の感情分析に挑戦! 筆者は、NGCカタログイメージからコンテナ起動し、Google ChromeからJupyterLabに接続して実行しています。 Codeを順に紹介してゆきます。 1. ハンズオンに倣って、BERTモデルとMeCab辞書で感情分析器をインスタンス化 from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification model = AutoModelForSequenceClassification.from_pretrained("/home/jovyan/bert-base-japanese-sentiment") tokenizer = AutoTokenizer.from_pretrained("/home/jovyan/bert-base-japanese-whole-word-masking", word_tokenizer_type="mecab", mecab_kwargs={"mecab_option": "-d /home/jovyan/mecab/ipadic -r /dev/null"}) sentiment_analyzer = pipeline("sentiment-analysis", model=model, tokenizer=tokenizer) 2. ニュース記事のCSVファイルをDataFrameに読込み import pandas as pd # CSVファイルからDataFrameを生成 df = pd.read_csv('livedoor_news/livedoor_news_topic_2022-01-01.csv', encoding='utf-8') df.head(5) 中身はこんな感じ。 url id title summary 0 https://news.livedoor.com/topics/detail/214452... 21445293 「うる星やつら」が36年ぶりにTVアニメ化 フジ・ノイタミナ枠で フジテレビの深夜アニメ枠「ノイタミナ」で放送されるとのこと 1 https://news.livedoor.com/topics/detail/214463... 21446393 「コミケ」の献血に長蛇の列で大盛況「オタクの鑑」など感心の声 東京都赤十字血液センターはTwitterで長蛇の列の様子を掲載し、感謝 2 https://news.livedoor.com/topics/detail/214474... 21447477 「逃走中」HIKAKINが逃走成功で150万円獲得 視聴者から祝福の声 賞金である150万円を獲得することとなり「やった〜!」と歓喜 3 https://news.livedoor.com/topics/detail/214458... 21445857 声優の飯田里穂が一般人との結婚を報告「あたたかく見守っていただけたら」 ツイッターでは直筆書面にて「一般の方と入籍いたしました」と伝えている 4 https://news.livedoor.com/topics/detail/214453... 21445372 餅がのどに詰まったら4分勝負、やるべき3つの対処法 NG行動は? 人間は窒息すると4分で脳に影響が出るため、その場での対処が重要だという 3. ニュース記事タイトルを対象に感情分析 result = [sentiment_analyzer(title)[0] for title in df['title'].to_list()] for x, y in zip(result, df['title'].to_list()): x.update({'title': y}) 4. 感情分析結果(戻り:Dict型)のリストからDataFrameを生成し、ニュース記事のDataFrameと結合 df = pd.merge(df, pd.DataFrame(result), on='title') df.head(5) 中身はこんな感じ。 url id title summary label score 0 https://news.livedoor.com/topics/detail/214452... 21445293 「うる星やつら」が36年ぶりにTVアニメ化 フジ・ノイタミナ枠で フジテレビの深夜アニメ枠「ノイタミナ」で放送されるとのこと ポジティブ 0.877783 1 https://news.livedoor.com/topics/detail/214463... 21446393 「コミケ」の献血に長蛇の列で大盛況「オタクの鑑」など感心の声 東京都赤十字血液センターはTwitterで長蛇の列の様子を掲載し、感謝 ポジティブ 0.848410 2 https://news.livedoor.com/topics/detail/214474... 21447477 「逃走中」HIKAKINが逃走成功で150万円獲得 視聴者から祝福の声 賞金である150万円を獲得することとなり「やった〜!」と歓喜 ポジティブ 0.826164 3 https://news.livedoor.com/topics/detail/214458... 21445857 声優の飯田里穂が一般人との結婚を報告「あたたかく見守っていただけたら」 ツイッターでは直筆書面にて「一般の方と入籍いたしました」と伝えている ポジティブ 0.924203 4 https://news.livedoor.com/topics/detail/214453... 21445372 餅がのどに詰まったら4分勝負、やるべき3つの対処法 NG行動は? 人間は窒息すると4分で脳に影響が出るため、その場での対処が重要だという ポジティブ 0.862354 5. ポジティブ判定のニュース記事をスコア(ポジティブ度合い)の高い順にソートして表示 df[df['label'] == 'ポジティブ'].sort_values('score', ascending=False) 結果はこんな感じ。「ぱっと見た感じネガティブな気もしたけど、ポジティブに判定される記事もあるんだな」という印象。 url id title summary label score 30 https://news.livedoor.com/topics/detail/214459... 21445980 声優・井上麻里奈が青二プロダクションへ移籍「さらに精進して参ります」 「シグマ・セブンの皆様には感謝の気持ちで一杯」と前事務所に感謝 ポジティブ 0.962433 13 https://news.livedoor.com/topics/detail/214446... 21444622 天皇陛下と雅子さまがともに新年のビデオメッセージ 全文を紹介 陛下は、新型コロナウイルスや、発生から10年を迎えた東日本大震災に言及 ポジティブ 0.943239 20 https://news.livedoor.com/article/detail/21446... 21446256 ロイヤルホスト“苺”スイーツ発売へ! 贅沢なパフェ&ケーキなど全5種類が登場 ファミリーレストラン「ロイヤルホスト」は、1月12日(水)から、季節の… ポジティブ 0.934567 12 https://news.livedoor.com/article/detail/21446... 21446720 ピンク可愛い ガーナ新作アイス ロッテはチョコレート「ガーナ」ブランドの新作アイスクリーム「ガーナチョ… ポジティブ 0.930231 37 https://news.livedoor.com/topics/detail/214460... 21446051 「うる星やつら」が新作アニメ化 ラム役の声優・上坂すみれがコメント ヒロイン・ラム役を上坂すみれ、主人公・諸星あたる役を神谷浩史が担当 ポジティブ 0.929826 24 https://news.livedoor.com/article/detail/21446... 21446472 たこ壺に入った人気駅弁の「ひっぱりだこ飯」ドライカレーに 神戸の老舗駅弁メーカー淡路屋(本社、神戸市東灘区)は1月7日、名物駅弁… ポジティブ 0.926005 26 https://news.livedoor.com/article/detail/21444... 21444730 マイナポイント第2弾が本日(1月1日)スタート 当初は限定展開 マイナンバーカードの取得・利用促進を目的とした「マイナポイント」事業の… ポジティブ 0.925518 3 https://news.livedoor.com/topics/detail/214458... 21445857 声優の飯田里穂が一般人との結婚を報告「あたたかく見守っていただけたら」 ツイッターでは直筆書面にて「一般の方と入籍いたしました」と伝えている ポジティブ 0.924203 34 https://news.livedoor.com/article/detail/21444... 21444437 「ぐるナイ おもしろ荘」優勝は人力舎所属のピン芸人・ゆめちゃん 日本エレキテル連合、おかずクラブ、ぺこぱといったブレイク芸人の登竜門… ポジティブ 0.917474 28 https://news.livedoor.com/article/detail/21444... 21444423 アニメ『魔法科高校の劣等生』続編制作決定 特報映像も公開 アニメ『魔法科高校の劣等生』の続編が制作されることが決定した。12月31… ポジティブ 0.913538 15 https://news.livedoor.com/article/detail/21446... 21446073 古川登志夫&平野文『うる星やつら』新作アニメ化で喜び「楽しみにしてるっちゃ!」 『週刊少年サンデー』(小学館)で連載されていた漫画『うる星やつら』(… ポジティブ 0.910654 6 https://news.livedoor.com/article/detail/21447... 21447352 三大流星群のひとつ「しぶんぎ座流星群」 1月3日深夜が見頃 三大流星群のひとつ「しぶんぎ座流星群」 1月3日深夜が見頃2022/01/01 20:0… ポジティブ 0.906668 31 https://news.livedoor.com/article/detail/21444... 21444198 「SUMMER SONIC 2022」8月20日、21日に東京と大阪で開催へ 音楽フェス『SUMMER SONIC 2022』が今年8月20、21日に東京と大阪で開催さ… ポジティブ 0.895463 27 https://news.livedoor.com/article/detail/21446... 21446129 Amazonプライムビデオ1月の新作:「約束のネバーランド」「今日から俺は!!劇場版」「銀魂... 幅広い作品ラインアップが魅力のAmazon Prime Videoでは、毎月、新作の配信… ポジティブ 0.886706 21 https://news.livedoor.com/article/detail/21444... 21444375 「ジャニーズカウントダウン」でサプライズ、相葉雅紀が登場 ジャニーズ事務所所属のアイドルグループが多数出演する年越しライブ「ジャ… ポジティブ 0.886215 0 https://news.livedoor.com/topics/detail/214452... 21445293 「うる星やつら」が36年ぶりにTVアニメ化 フジ・ノイタミナ枠で フジテレビの深夜アニメ枠「ノイタミナ」で放送されるとのこと ポジティブ 0.877783 9 https://news.livedoor.com/topics/detail/214456... 21445695 大阪城とUSJを結ぶ遊覧船 2023年春にも定期運航が始まる見通し 23年春に大阪城公園の船着き場が完成する見込みで、これを活用して運航する ポジティブ 0.876632 19 https://news.livedoor.com/article/detail/21446... 21446116 仮面ライダー図鑑、期間限定で“寅年”仕様に タイガにオーズにゼロワン ジオウは“平成34年”に 仮面ライダーシリーズ公式ポータルサイトの人気コンテンツ「仮面ライダー… ポジティブ 0.872462 4 https://news.livedoor.com/topics/detail/214453... 21445372 餅がのどに詰まったら4分勝負、やるべき3つの対処法 NG行動は? 人間は窒息すると4分で脳に影響が出るため、その場での対処が重要だという ポジティブ 0.862354 23 https://news.livedoor.com/topics/detail/214462... 21446237 ニューイヤー駅伝で創部51年目のホンダが初優勝 6区で逆転首位 2区23位に落ちたが、6区でトップになり4時間51分04秒の記録で優勝した ポジティブ 0.861704 36 https://news.livedoor.com/topics/detail/214442... 21444220 12星座×血液型で見る2022年最強運ランキング 1位〜48位発表 1位は魚座×AB型で、思い切ったチャレンジが幸運を呼び寄せそう、とのこと ポジティブ 0.861353 25 https://news.livedoor.com/article/detail/21444... 21444170 映画「キングダム2」正式タイトル発表、スーパーティザーPVも解禁 2022年のはじまりとともに、実写映画『キングダム』の続編・パート2のタ… ポジティブ 0.850540 1 https://news.livedoor.com/topics/detail/214463... 21446393 「コミケ」の献血に長蛇の列で大盛況「オタクの鑑」など感心の声 東京都赤十字血液センターはTwitterで長蛇の列の様子を掲載し、感謝 ポジティブ 0.848410 14 https://news.livedoor.com/article/detail/21445... 21445327 23歳差の”恋心” ダニエル・ラドクリフがヘレナ・ボナム・カータ&#12540... 俳優のダニエル・ラドクリフ(32)は、「ハリー・ポッター… ポジティブ 0.836254 7 https://news.livedoor.com/topics/detail/214467... 21446778 身長や挙動テスト 経験者が語る「逃走中」のハンターの採用条件 ハンターの第1条件は身長180センチ以上で、モデルによく声がかかるという ポジティブ 0.836040 17 https://news.livedoor.com/article/detail/21447... 21447460 YouTube『Johnny′s Gaming Room』が本格始動 “参戦”メンバー13人を発表 ジャニーズのゲーム好きが集うYouTubeチャンネル『Johnny's Gaming Room… ポジティブ 0.832476 35 https://news.livedoor.com/article/detail/21444... 21444428 JR山陰線で倒木 約90人が車中泊 12月31日深夜、京都府内のJR山陰線で特急「きのさき17号」など列車3本が… ポジティブ 0.829401 2 https://news.livedoor.com/topics/detail/214474... 21447477 「逃走中」HIKAKINが逃走成功で150万円獲得 視聴者から祝福の声 賞金である150万円を獲得することとなり「やった〜!」と歓喜 ポジティブ 0.826164 32 https://news.livedoor.com/article/detail/21446... 21446689 『土方のスマホ』“正月の陣”放送決定 賀来賢人が坂本龍馬役 俳優の窪田正孝が土方歳三を演じ、昨年9月から10月にかけてNHK総合で放送… ポジティブ 0.825295 29 https://news.livedoor.com/topics/detail/214466... 21446653 JR松江駅で女性がけが 刃物を手にした男を現行犯逮捕 警察が現場に駆けつけたところ、けがをしている女性を発見 ポジティブ 0.816801 11 https://news.livedoor.com/topics/detail/214466... 21446661 「笑点」5年7カ月ぶりの新メンバーは桂宮治 正月特番内で発表 大喜利新メンバーとして、桂宮治が加わることが発表された ポジティブ 0.796677 22 https://news.livedoor.com/topics/detail/214454... 21445446 滋賀の県道高架下で焼けた遺体発見 仰向けの状態で死後数日が経過 遺体は仰向けの状態で、死後数日経過しているという ポジティブ 0.781807 8 https://news.livedoor.com/topics/detail/214474... 21447412 「ニューイヤー駅伝」優勝旗は授与されず 2021年王者の富士通が紛失 表彰式では優勝旗の授与はなく、優勝杯や賞状などがHondaに贈られた ポジティブ 0.772829 10 https://news.livedoor.com/topics/detail/214455... 21445513 鉛筆全体でHBの売り上げが激減 書写以外の小学校の授業ではBや2B推奨 1999年はHBが43%、2Bが22%だったが、2019年には2Bが51%、HBが20%に ポジティブ 0.743005 6. ネガティブ判定のニュース記事をスコア(ネガティブ度合い)の高い順にソートして表示 df[df['label'] == 'ネガティブ'].sort_values('score', ascending=False) 結果はこんな感じ。「ネガティブに判定されるのも納得」という印象。 感情分析器がポジティブ判定を出しがちなのか、お正月というタイミングなのか、ネガティブ判定のニュース記事は少ないような。。。 url id title summary label score 18 https://news.livedoor.com/article/detail/21444... 21444511 マルフォイ役で知られるトム・フェルトン 母親から禁止されたこと 「ハリー・ポッター」シリーズのドラコ・マルフォイ役などで知られる俳優の… ネガティブ 0.926198 33 https://news.livedoor.com/topics/detail/214463... 21446317 オミクロン株、千葉で初の市中感染か 2週間以内の海外渡航歴のない2人感染 2人とも2週間以内の海外渡航歴などがないため、市中感染の疑いがあるという ネガティブ 0.670254 5 https://news.livedoor.com/article/detail/21446... 21446976 都内で6人のどに餅詰まらせ搬送…2人死亡 元日の1月1日、東京都内で6人が餅をのどに詰まらせて救急搬送され、この… ネガティブ 0.652022 16 https://news.livedoor.com/article/detail/21445... 21445030 2022年の年明けを告げる 初日の出 あけましておめでとうございます!2022年がスタートしました。太平洋側は晴… ネガティブ 0.582705 まとめ 公開されたBERTモデルを利用して、ニュース記事を感情分析する方法を紹介しました。 簡単に利用できるのにもかかわらず、その結果は想像以上に納得感のあるものと思いました。 Open Resources【公開資源】https://www.nlp.ecei.tohoku.ac.jp/research/open-resources/ ↩ 日本語評価極性辞書 http://www.cl.ecei.tohoku.ac.jp/Open_Resources-Japanese_Sentiment_Polarity_Dictionary.html ↩ PyTorch Release 20.11 https://docs.nvidia.com/deeplearning/frameworks/pytorch-release-notes/rel_20-11.html#rel_20-11 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

こんにちは

こんにちは
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

hazimemashitte

初めまして
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure Databricks で 個人データ保護のために UUID を利用してみました

概要 個人を特定できるデータをそのまま公開するわけにはいかないので、その個人毎にユニークなランダム文字列(UUID利用)を付与することにより、データ処理できるようにする方法となります。 ローカル環境 macOS Monterey 12.1 python 3.8.3 Azure CLI 2.28.0 前提条件 Azure環境がすでに用意されていること(テナント/サブスクリプション) ローカル環境に「azure cli」がインストールされていること Azure Databricks の Workspace 上で Notebook が稼働できていること。 事前準備 データファイルのアップロード 対象の Workspace の Azure Databricks ポータルで [Import & Explorer Data] を選択します 「Drop files to upload, or click to browse」に対象のファイルをドラッグ&ドロップします 「DBFS」タブを選択し、「FileStore - tables」フォルダ配下にドロップしたファイル(SE_Account.csv)を確認することができます Notebook の実装 対象ファイルの読込(データの抽出) cmd_1 # 対象データの確認 display(dbutils.fs.ls("/FileStore/tables/")) # csv型式のデータファイルをDataFrameとして読取る sdf = spark.read.csv('/FileStore/tables/SE_Account.csv', header='true', inferSchema='true', encoding='utf-8') display(sdf) SE毎にユニークなUUIDの付与 cmd_2 import uuid # PySpark Dataframes から Pandas への変換 pdf = sdf.toPandas() # カラム['U_Name']を新規作成し、UUIDを割り当てる pdf['U_Name'] = [str(uuid.uuid4()) for x in range(len(pdf))] display(pdf) # 不必要なカラムの削除後、Pandas から PySpark Dataframes への変換 df = spark.createDataFrame(pdf.drop(['J_Name', 'E_Name', 'Name', 'mailaddress'], axis = 1)) display(df) まとめ データ分析を実施するにあたり、データの匿名性が大事ということに気が付き、今回の記事を記載。ただ、かなり社内よりの内容なので、忘備録のような扱いになってしまいました、、、、、 参考記事 以下の記事を参考にさせていただきました。感謝申し上げます pandas DataFrameの新しい列にuuidを追加します Python Random Module: Generate Random Numbers and Data
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

FastAPI+React+DockerでQiitaみたいなサイトを作ってみたい -7日目-

目次 1日目 - 初期構成 2日目 - /docsの作成 3日目 - DBとModelの作成 4日目 - CRUDの作成 5日目 - ユーザの認証の追加 6日目 - テストの導入 7日目 - バリデーションの追加 各Modelのバリデーションを追加し、それぞれのエラーハンドリングを適応させていく。 Userモデルのバリデーション api/v1/models/user.py # ~~~ from sqlalchemy.orm import relationship,validates # ~~~ @validates("login_id") def check_user_login_id(self,key,v): if v == None: raise ValueError("LoginId must not be null") if v == "": raise ValueError("LoginId must not be empty string") if not v.replace('-','').replace('_','').isalnum(): raise ValueError("LoginId can only contain letters, numbers, _, and -") if v[0] == "-" or v[0] == "_": raise ValueError('LoginId cannot start with _ or -') return v @validates("name") def check_user_name(self,key,v): if v == None: raise ValueError("Name must not be null") if v == "": raise ValueError("Name must not be empty string") return v # ~~~ ただこの場合、passwordはモデルに入るときにはhashにされてしまうのでここではバリデーションできない。 なので一旦crudsファイル内でバリデーションする。(絶対もっといい方法がある。。。) api/v1/cruds/user.py # ~~~~ def create_user(db:Session,user:user_schema.UserCreateRequest): if len(user.password) < 8: raise ValueError("Password require at least 8 characters") if not user.password.isalnum(): raise ValueError("Password can only contain letters, numbers") password_hash = get_password_hash(user.password) new_user = User( login_id=user.login_id, name=user.name, description=user.description, password_hash=password_hash ) # ~~~~ これで各部でバリデーションによるエラーを起こすところまでできたので、あとはこのエラーをルーター部分でキャッチする。 api/v1/routers/user.py # ~~~ @router.post('/users',response_model=user_schema.User) def create_user(user:user_schema.UserCreateRequest,db:Session = Depends(get_db)): db_user = user_crud.get_user_by_login_id(db,user.login_id) if db_user: raise HTTPException(status_code = 400,detail = "LoginId already exists") try: new_user = user_crud.create_user(db,user) except ValueError as e : raise HTTPException(status_code = 400,detail = str(e)) return new_user # ~~~ これでバリデーションに引っかかった場合も400でエラー内容を返すことができる。 pydanticでもバリデーションが行えるので、そっちにもバリデーションを追加しておくとより良いかも。 とりあえずこれでbackend側の構築はOK。(足りない場合も今までの実装方法で対応できる。。はず。。。) 次からはフロントエンドを作っていくが、Next.jsを利用した構築にチャレンジしてみようと思うので、次回はまた時間がかかりそう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ちょこっと機械学習入門の人向けランダムフォレスト回帰でボストン住宅価格を予測

ちょっとだけ機械学習をやってみたい方のための、ちょこっとPythonコードです。 scikit-learnに入っている学習用データセット、ボストンデータセットをランダムフォレスト回帰を使って学習・予測するコードです。 Anacondaをインストールしてください 最初に入っていない人はAnacondaのIndividual版をダウンロードしてインストールしましょう。 https://www.anaconda.com/products/individual ダウンロードが完了したらインストーラーを起動して、デフォルトのまま次へ次へでインストールしてしまって大丈夫です。 Jupyter notebookを起動 Anaconda Navigatorを起動して、ホーム画面にあるJupyter notebookを起動しましょう。 ブラウザが開いたら、右上のメニューの「新規」から「Python 3」を選んで新しいノートブックを作りましょう。 コードを書いて「Run」ボタンで実行 最初の入力フォームに以下のコードを書いて(最悪コピペして)実行してください。 読み込まれたボストンデータセットが表示されます。 # ランダムフォレスト回帰をインポート from sklearn.ensemble import RandomForestRegressor # テストデータセット分割関数 from sklearn.model_selection import train_test_split # 勉強用サンプルデータセットのインポート from sklearn import datasets # ボストンデータセットを読み込み boston = datasets.load_boston() boston 次に読み込んだボストンデータセットのデータと正解ラベルをそれぞれ機械学習用と検証用に分割します。 train_test_split関数を使っています。 # 訓練用とテスト用にデータ分割 train_x, test_x, train_y, test_y = train_test_split( \ boston.data, boston.target, test_size=0.2) そしてランダムフォレスト回帰のクラスをインスタンス化して学習させ、検証用データを予測してみましょう。 # ランダムフォレスト回帰クラスのインスタンス作成 forest = RandomForestRegressor() # トレーニング用データと正解ラベルで学習 forest.fit(train_x, train_y) # 学習したモデルインスタンスでテストデータの値を予測 preds = forest.predict(test_x) # 結果をデータフレームで結合して表示 import pandas as pd df_result = pd.DataFrame(test_y, columns=["実売価格"]) df_result["予測価格"] = preds df_result 予測と実際の正解の価格を見比べてみましょう。 # グラフ描画ライブラリをインポート import matplotlib.pyplot as plt %matplotlib inline # 折れ線グラフで表示 plt.figure(figsize=(20,10)) plt.plot(test_y,label="True") plt.plot(preds, label="predicted") とりあえずやってみるだけの人はこれをやってみて、簡単であることを実感し、興味を持っていただけると幸いです。 以上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python+サーバレス開発を勉強する中でzappaがデプロイするリソースについて気になったので調べてみた

はじめに 動かして学ぶ!Pythonサーバレスアプリ開発入門 著:本田崇智 を読み Python+サーバレスを学習する中で 作成したアプリケーションをAWS環境にデプロイしてくれるzappaというツールが どのようにApiGatewayとLambdaを構築したのか気になり、確認した際のメモ デプロイするアプリケーション構成 参考書のchapter9が完了した時点での構成をざっくり抜粋して書くと、以下のようになる application ├sever.py # アプリ起動プログラム ├zappa_settings.json # zappa設定ファイル  └ flask_blog ├__init__.py # 初期処理プログラム ├config.py # アプリケーション設定プログラム ├models/ # モデルプログラム群 ├static │ └style.css # CSS定義ファイル ├templates/ # HTMLファイル群 └ views # ビュープログラム群 zappaでデプロイしたリソース確認 以下コマンドでデプロイ $ zappa deploy dev ApiGatewayの確認 Lambdaの確認 でてきた疑問点 HTMLファイルの実体どこにあるんだ? よく目にするのは静的ファイルはs3に格納して表示するパターン lambdaのソースコードどうなっている? 以下のようにコードのファイルサイズが大きすぎると表示されて、 コンソールからはファイルが確認できない the deployment package of your Lambda function "application-dev" is too large to enable inline code editing. However, you can still invoke your function. 調べた内容 AWS Webアプリのサーバレスアーキテクチャ例 そもそもサーバレスでどういったアーキテクチャが考えられるのか調べてみたところ やっぱり静的コンテンツをS3(またはs3を内包するamplify)に格納しておいて 動的コンテンツはApigatewayとlambdaを使うといったアーキテクチャは公開されていた s3 + ApiGateway + Lambda 参考 初めてのサーバーレスウェブアプリケーションを構築する CloudFrontを使って設計するパターンもあるよう 参考: AWS Well-Architected フレームワーク ウェブアプリケーション Powering HIPAA-compliant workloads using AWS Serverless technologies サーバーレス LAMP スタック – Part 3: Webサーバーの置き換え ここまで調べた段階で、zappaが作成するs3を確認してみたが何も入ってなかった、、、 どうやら作成されたs3は一時的に使用しているだけのようで 調べてきたアーキテクチャとは違う模様 上記から結論づけるにHTMLもlambdaから返却している ->Flaskで作成して動的にHTMLを返すアプリケーションなので当たり前か 後述するがlambdaのソースコードを確認しても 作成したアプリケーションのコードが全部含まれていた Lambdaのソースコード取得方法 Lambdaのソースコードは調べたところ以下コマンド取得できた (コンソールから アクション->関数のエクスポートでも取得できるみたい) $ aws lambda get-function --function-name [function名] --query 'Code.Location' | xargs curl -o [出力ファイル名指定(例 xxx.zip)] 取得した結果を確認したところ デプロイするアプリケーション構成に記載したファイルもまるっと含まれていた 終わりに いつかサーバレスアプリケーションを作ってみたいなと思っていたので Pythonの学習に加えてサーバレスのアーキテクチャパターンを調査できたのはよかった SPAとMPAの違いもろくに分かっていなかったので Webアプリ開発の理解が乏しすぎることを再認識しました、、、
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WindowsのPowerShellにTensorFlow-GPUを入れて動かした~い

今までPyTorchを使ってきたのですが、TensorFlow For Cを触ってみたくなったので、Windowsでセットアップを行いましたが...? 環境 Windows 10 GPU NVIDIA GeForce RTX 3060 LapTop PowerShellで動かしたい Instration 基本はここ見ればわかるらしいけど恐ろしくざっくりしている。 (注)バージョンが合わないと容赦なく動かなくなっちゃうので注意!! Visual C++ Build Tools 2019 をインストールする Visual Studio のダウンロード サイトに移動します。 [再頒布可能パッケージおよびビルドツール] を選択します。 以下をダウンロードしてインストールします。 Microsoft Visual C++ 2019 再頒布可能ファイル Microsoft Build Tools 2019 GPUに対応するドライバーのバージョンを確認 NVIDIAドライブダウンロードサイトにアクセスし対応するドライバーを確認 ドライバーのバージョンをメモ ドライバーを入れてない場合はダウンロード&インストール 対応するCUDAとcuDNNをチェックしてダウンロード 先ほどメモったドライバーのバージョンから利用可能なCUDA Toolkitのバージョンを確認する NVIDIA CUDA Toolkit Release Note ドライバーのバージョンより小さいバージョンに対応するCUDAのバージョンを確認する Build from source on Windows  |  TensorFlow ここからGPUが利用可能であるCUDAのバージョンを起点として、構築するPython, CUDA, cuDNN, TensorFlowのバージョンを決定する(今回→TensorFlow2.6, Cuda11.4, cuDNN8.2, Python3.9にしました) TensorFlowのインストール pip install tensorflow==2.6.0 CUDA Toolkitのインストーラーのダウンロードとインストール CUDA Toolkit Archive cuDNNのダウンロード cuDNN Archive for CUDAのバージョンに注意 5.をダウンロードしたら中身を全部 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v<バージョン>内にコピペ Bazelのインストール Chocolateyのインストール Chocolateyで、まっさらWindowsに一気にソフトをインストール Bazelのインストール choco install bazel MSYS2のインストール chocolateyでインストールすると楽ちん choco install msys2 終わり! 多分本家より気持ちかんたんにインストールできると思います。 Pytorchと比べて手順が多すぎますね.. エラー Windows10にTensorFlow GPU環境を構築し、学習を回そうとしたら次のエラーがでた tensorflow.python.framework.errors_impl.AlreadyExistsError: Another metric with the same name already exists.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TensorFlowでAlreadyExistsErrorがでた

エラー内容 環境 * Windows10 x PowerShell * python3.9 * TensorFlow=2.6.0 * CUDA 11.4 * cuDNN 8.2 Windows10にTensorFlow GPU環境を構築し、学習を回そうとしたら次のエラーがでた tensorflow.python.framework.errors_impl.AlreadyExistsError: Another metric with the same name already exists. 同名のメトリクスを2回読み込んでるよ!的なやつ。 同じモジュールを2回importするとでるやつだけど、心当たりがない。 調べてみるとTensorFlowが持ってるKerasのバージョンとオリジナルKerasのバージョンのミスマッチがあると起こるらしい。(クソすぎでは) pip listをするとkeras==2.7.0だったので、 pip install keras==2.6.*をすると動くようになった
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pyenv + venv + VSCodeでPython環境構築

環境 macOS Monterey Homebrew 3.3.9 pyenv 2.2.3 VSCode 1.63.2 前提 ログインシェルはzsh Homebrewがインストール済 VSCodeとPython用の拡張機能(Python)がインストール済み 構築手順 pyenvをインストール https://github.com/pyenv/pyenvのインストール手順を参考にしました。 Homebrewでpyenvをインストール brew update brew install pyenv zshの設定 echo 'eval "$(pyenv init --path)"' >> ~/.zprofile echo 'eval "$(pyenv init -)"' >> ~/.zshrc ここで一旦ターミナルをrestart 依存パッケージをインストール brew install openssl readline sqlite3 xz zlib インストールはこれで完了 インストールできるPythonのバージョン一覧 pyenv install --list バージョンを指定してpythonをインストール pyenv install 3.10.0 pyenv installすると configure: error: C compiler cannot create executables のエラーになりました。 こちらを参考にさせていただき、XcodeとCommand line toolsをアップデートしたら解決しました。 https://charmie11.hatenablog.com/entry/2021/07/20/101528 インストール済みバージョンの確認 pyenv versions 全てのシェルで利用するPythonのバージョンを指定 pyenv global 3.10.0 カレントディレクトリ以下で利用するPythonのバージョンを指定 pyenv local 3.10.0 現在のシェルでのみ利用するPythonのバージョンを指定 pyenv shell 3.10.0 バージョン設定の優先順位は、shell > local > global の順。 現在のバージョンを確認 pyenv version venvで仮想環境を作成 venvはpython3.3から標準搭載されているそうです。 cd [/path/to/project] #プロジェクトのディレクトリに移動 pyenv local 3.10.0 #カレントディレクトリで使用するpythonのバージョンを指定 python -m venv [/path/to/new/virtual/environment] #仮想環境の作成 作成した仮想環境を有効化する必要がありますが、VSCodeを使う場合は、自動で有効化されます。 手動で有効化する場合は、 cd [/path/to/new/virtual/environment]# 仮想環境のディレクトリに移動 source ./bin/activate# 有効化 停止する場合は、 deactivate VSCodeで開発環境作成 拡張機能「Python」が必要です。 以降は、インストールされている前提の手順です。 プロジェクトのディレクトリ[/path/to/project]をVSCodeで開く ファイル > フォルダーを開く ディレクトリ配下にpythonファイルが無い場合は作成。 例えば、test.pyなど。中身は空でも大丈夫みたいです。 pythonファイルを選択すると、自動的に仮想環境がactivateされるようで、VSCodeの画面一番下の青いステータスバーの左側に Python 3.10.0 64-bit ('[仮想環境]':venv) のように表示される。 ターミナル > 新しいターミナル でターミナルを開くと、仮想環境が有効になったプロンプトになっている。 以下で、仮想環境になっていることを確認。 which python which pip
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Python]docstringのNumPyスタイルの書き方

docstringのNumPyスタイルの書き方について簡単にメモしておく。 docstringとは、関数やクラスに対する説明を記載する方法の1つ。docstringを記載することで、ドキュメントをHTML形式で自動生成することもできる。 NumPyスタイル 基本的な書き方 基本的な書き方は、reStructuredTextスタイルやGoogleスタイルと同じで、関数やクラス定義の先頭に'''または"""で説明を囲む形で記載する。 def my_function(): """<関数の説明を記載>""" スタイルごとの違いとしては、引数や返り値などの書き方が異なっている。 記載サンプル NumPyスタイルでの引数や返り値などの記載例は以下のようになる。 サンプルで使用していないセッションについては下記ページを参照。 https://numpydoc.readthedocs.io/en/latest/format.html#sections class Sample(): """<クラスの概要> <クラスの詳細説明>   Attributes   ---------   <属性名> : <型>    <説明> """ def my_function(): """<関数の概要> <関数の詳細説明> Parameters    --------- <変数名> : <型> <説明> Returns    --------- <戻り値名>: <型> <説明> Raises    --------- <例外名> <説明> """ <関数の処理内容>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[py2rb] 辞書 の四則演算

はじめに 移植やってます。 ( from python 3.7 to ruby 2.7 ) Dict (Python) 辞書の四則演算が必要らしいのですが、辞書を継承してメソッドを追加しているみたいです。 Hash (Ruby) class H < Hash def initialize self.default = 0 end def +(other) case other when Hash other.each do |k, v| self[k] += v end when Integer || Float self.each do |k, _| self[k] += other end end end def *(other) case other when Hash other.each do |k, v| self[k] *= v end when Integer || Float self.each do |k, _| self[k] *= other end end end end h = H.new h + {'A' => 1} h + {'A' => 1, 'B' => 1} p h # {"A"=>2, "B"=>1} h + 3 p h # {"A"=>5, "B"=>4} h * {'B' => 2, 'C' => 2} p h # {"A"=>5, "B"=>8, "C"=>0} h * 2 p h # {"A"=>10, "B"=>16, "C"=>0} そられしくなりました。 本来であれば交換則で、例えば、Integer * Hashも必要なのですが。 もしかしたら、gemsやnumpy/scipyにあるかもしれませんね。 メモ Python の 辞書 の四則演算 を学習した 百里を行く者は九十里を半ばとす
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Project Euler】Problem 26: 循環小数

本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 26:循環小数 原文 Problem 26: Reciprocal cycles 問題の要約:分母が1000以下の単位分数(分子が1のもの)で循環小数となる循環節が最も長いものの分母を求めよ 循環小数(Wikipedia)に詳細がありますが、分数は有限小数(Repeating decimal)か循環小数(Rerminating decimal)になり、ここでは$1/d$の形の単位分数で循環小数になるものの循環節の長さを求める問題です。 まず単位分数を小数に変換するプログラムdecUnitFracです。Wikipediaの筆算にあるように「余りに同じ数が現れた時点で、繰り返しに入ったことがわかる」ので余りをすべてリストに入れてチェックします。小数部分の文字列(dec)と循環節の始まりの位置(rs)を返します。有限小数の時はrs=-1とします。問題の例の続きのd=11~23を表示して正しそうなことを確認します。 # Input: # d - denominator of a unit fraction # Output: # dec - decimal string # rs - starting positon of reptend, rs<0 if terminating decimal: def decUnitFrac(d): m, mlist, dec = 1, [], "" while True: if m in mlist: # check the same remainder in the list) return (dec, mlist.index(m)) mlist.append(m) # remember all reminders dec += str((m*10) // d) # repeating part string m = (m*10) % d # m: reminder if m==0: return (dec, -1) # terminating decimal for d in range(11, 23+1): (dec, rs) = decUnitFrac(d) if rs>=0: print(f"1/{d}: 0.{dec[:rs]}({dec[rs:]}) ") else: print(f"1/{d}: 0.{dec}") #1/11: 0.(09) #1/12: 0.08(3) #1/13: 0.(076923) #1/14: 0.0(714285) #1/15: 0.0(6) #1/16: 0.0625 #1/17: 0.(0588235294117647) #1/18: 0.0(5) #1/19: 0.(052631578947368421) #1/20: 0.05 #1/21: 0.(047619) #1/22: 0.0(45) #1/23: 0.(0434782608695652173913) 分母が素数の単位分数の循環小数 問題を解くだけであればこれを1000まで回して循環節の長さの最大値を求めれば良いのですが。いろいろ気付いたのでちょっと考察してみます。 まず循環小数の結果を眺めてみると、分母が素数の時に循環節の長くなっているのが分かります。そこで素数の場合だけ表示してみます。$rc$が循環節の長さです。 1/13: rc=6, 0.(076923) 1/17: rc=16, 0.(0588235294117647) 1/19: rc=18, 0.(052631578947368421) 1/23: rc=22, 0.(0434782608695652173913) 1/29: rc=28, 0.(0344827586206896551724137931) 1/31: rc=15, 0.(032258064516129) 1/37: rc=3, 0.(027) 1/41: rc=5, 0.(02439) 1/43: rc=21, 0.(023255813953488372093) 1/47: rc=46, 0.(0212765957446808510638297872340425531914893617) すると素数でも長いもの(17,19,23,29,47)と短いもの(13,31,37,41,43)があり、長いものは必ず$rc=d-1$となっていることが分かります。 循環節の長さと位数・原始根 これはなにか規則性がと思って調べてみたら、英語のWikipedia Repeating_decimal-Fractions with prime denominatorsに「1/pの循環節の長さは10 mod pの位数(order)に等しい」との記述がありました。位数はかなり難しい概念ですが「原始根の定義と具体例(高校生向け)」の説明が比較的分かりやすいと思います $r,r^2 ,r^3 ,⋯ $と $r$ のベキを増やしていき,はじめて「$p$ で割った余りが $1$ となる」ような指数のことを $r$ の位数と言います。 これをもとに以下のようにプログラムで確認すると、位数(初めて1になる指数)はorder(10)で表示されていますが、循環節の長さと一致していることが分かります。 for p in [13,17,19,23,29,31,37,41,41,43]: pm = [pow(10,i,p) for i in range(1,p)] print(f"p={p}, order(10)={pm.index(1)+1}, {pm}") p=13, order(10)=6, [10, 9, 12, 3, 4, 1, 10, 9, 12, 3, 4, 1] p=17, order(10)=16, [10, 15, 14, 4, 6, 9, 5, 16, 7, 2, 3, 13, 11, 8, 12, 1] p=19, order(10)=18, [10, 5, 12, 6, 3, 11, 15, 17, 18, 9, 14, 7, 13, 16, 8, 4, 2, 1] p=23, order(10)=22, [10, 8, 11, 18, 19, 6, 14, 2, 20, 16, 22, 13, 15, 12, 5, 4, 17, 9, 21, 3, 7, 1] p=29, order(10)=28, [10, 13, 14, 24, 8, 22, 17, 25, 18, 6, 2, 20, 26, 28, 19, 16, 15, 5, 21, 7, 12, 4, 11, 23, 27, 9, 3, 1] p=31, order(10)=15, [10, 7, 8, 18, 25, 2, 20, 14, 16, 5, 19, 4, 9, 28, 1, 10, 7, 8, 18, 25, 2, 20, 14, 16, 5, 19, 4, 9, 28, 1] p=37, order(10)=3, [10, 26, 1, 10, 26, 1, 10, 26, 1, 10, 26, 1, 10, 26, 1, 10, 26, 1, 10, 26, 1, 10, 26, 1, 10, 26, 1, 10, 26, 1, 10, 26, 1, 10, 26, 1] p=41, order(10)=5, [10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1] p=41, order(10)=5, [10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1, 10, 18, 16, 37, 1] p=43, order(10)=21, [10, 14, 11, 24, 25, 35, 6, 17, 41, 23, 15, 21, 38, 36, 16, 31, 9, 4, 40, 13, 1, 10, 14, 11, 24, 25, 35, 6, 17, 41, 23, 15, 21, 38, 36, 16, 31, 9, 4, 40, 13, 1] ここで元の問題に戻って、なるべく循環節の長いものすなわち位数が最大(p-1)のものを探せばよいということになりますが、前出のリンクの以下の定義によると結論として10が素数pの原始根となるものを探せばよいことが分かります。 原始根は 「位数が $p-1$ であるもの」と簡潔に表現できます。 10が原始根になる素数p 原始根の求め方は位数を求めて$p-1$となるものでも良いのですが、sympyにis_primitive_rootというその物があるのでそれを使います。したがって問題の答えは、 1000から大きい順に素数をリストして初めて10が原始根になる素数が、循環節の長さが最大のものということになります。 素数の逆順の生成にやはりsympyのprevprimeを使うと以下のようになります。これであれば1000が$10^6$になっても1秒以内に答えが出せます。 from sympy.ntheory import is_primitive_root from sympy import prevprime p = 1000 while True: p = prevprime(p) if is_primitive_root(10, p): break print(f"Answer : {p}") (開発環境:Google Colab)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Project Euler】Problem 24: 辞書式順列

本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 24. 辞書式順列 原文 Problem 24: Lexicographic permutations 問題の要約:0123456789を並べ替えたものを辞書式順列にしたとき$10^6$番目を求めよ 安易ですが、itertoolsのpermutationは「 iterable に応じた辞書式順序で出力されます」とのことなのでそのまま使えます。一応"012"で確認します。 import itertools for n, perm in enumerate(itertools.permutations("012"),1): print(f"{n}: {''.join(perm)}") #1: 012 #2: 021 #3: 102 #4: 120 #5: 201 #6: 210 確認できたので、$10^6$番目の値を出力します。 import itertools for n, perm in enumerate(itertools.permutations("0123456789"),1): if n >= 10**6: break print(f"Answer : {''.join(perm)}") (開発環境:Google Colab)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Project Euler】Problem 23: 過剰数の和

本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 23. 過剰数の和 原文 Problem 23: Non-abundant sums 問題の要約:過剰数の和にならない数の合計を求めよ 過剰数(Abundant number)(Wikipedia)の定義はこちら。今回は2つの過剰数の和で表せない数を列挙して合計を求めよと言うもの。問題では28123より大きい数は必ず和で表せるとありますが、Wikipediaによると20161が最大のようです。 過剰数の判定は「Problem 21: 友愛数」で作った関数sumpdivを使います。重複組合せのitertools.combinations_with_replacementを使ってすべての2つの数の和を求めて、その数をリストから除いて残った数を合計します。 import itertools N = 20161 # create the list of abundunt numbers abn = [i for i in range(1, N+1) if sumpdiv(i)> i] abn2sum = [i for i in range(N+1)] # list of all natural numbers for i, j in itertools.combinations_with_replacement(abn, 2): if i+j<=N: abn2sum[i+j] = 0 # erase the number by adding 2 abundant numbers combination print(f"Answer: {sum(abn2sum)}") 過剰数の和にならない数は数が大きくなると減っていくみたいなので、グラフを書いてみました。確かにn=10000を超えるとかなり少なくなっていのが分かります。描画プログラムは下にあります。 import matplotlib.pyplot as plt fig = plt.figure() ax = fig.add_subplot(1,1,1) abn2sumlist = [abn2sum[i] for i in range(1,N+1) if abn2sum[i]>0] ax.hist(abn2sumlist, bins=300, range=(0,N)) ax.set_title('Numbers that can not be written as the sum of two abundant numbers ') ax.set_xlabel('n') ax.set_ylabel('Numbers') fig.show() (開発環境:Google Colab)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonで祭日を取得する

以前、PHPで祭日の一覧を他のサイトから取得してそれを表示するというお仕事をしたことがあるのですがpythonでは祭日をリストアップしてくれるライブラリがあったので非常に良いなと思い忘備録として記述します。 と言ってもやることは簡単。 pip install holidays をして import holidays for date, name in holidays.Japan(years=[2022]).items(): print(date, name) 2022-01-01 元日 2022-01-10 成人の日 2022-02-11 建国記念の日 2022-02-23 天皇誕生日 2022-03-21 春分の日 2022-04-29 昭和の日 2022-05-03 憲法記念日 2022-05-04 みどりの日 2022-05-05 こどもの日 2022-07-18 海の日 2022-08-11 山の日 2022-09-19 敬老の日 2022-09-23 秋分の日 2022-10-10 スポーツの日 2022-11-03 文化の日 2022-11-23 勤労感謝の日 という出力結果になります。 yearsには[2020, 2021, 2022] などリストで対象年を入れるとその分のリストが出力されます。 Japanのところを他の国の名前にするとその国の祭日が出てきます。 USやCNなどを入れて試してみて下さい。 参考サイト https://pypi.org/project/holidays/0.1/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

高校の情報が一新されるので調べてみた!!!!!

高校の授業での情報の学習内容が変わると知ってどのような内容なのかを調べてみました。 この制度が適用されるのが来年度から入学してくる高校生からで、「情報ⅰ」「情報ⅱ」という 2 つの教科に分かれていました。 情報ⅰとは? 情報ⅰは共通履修科目で内容としては情報ⅱの基礎で IT パスポートの内容+データサイエンス+プログラミングみたいな印象を受けました。それに加えて日常生活での SNS の扱い方や情報デザインに関してもここで学習するようです。 情報ⅱとは? 情報ⅱは選択した人のみ学習する内容なので選択した人のみが学習する内容になっています。組織におけるセキュリティ対策や重回帰分析など発展した内容になっていて、知識に加えて、自発的に考える内容もあります。数学の知識や実際に使われている開発環境を使っており、より専門的により実用的な内容になっています。 調べてみて思ったこと 高校生の段階でここまでの知識がつくと、これからの学生のレベルも大きく変わってくるほど内容が濃くて、私たちにとってはもっと頑張らなくては!!!と思わせるような内容でした。自分ももっと危機感を持って勉強していきたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

論文の勉強5 DenseNet

論文の勉強をメモ書きレベルですがのせていきます。あくまでも自分の勉強目的です。 構造部分に注目し、その他の部分は書いていません。ご了承ください。 本当にいい加減・不正確な部分が多数あると思いますのでご理解ください。 今回は、以下の論文のDense Netの実装を行います。 タイトル:Densely Connected Convolutional Networks DenseNets 構造 $\boldsymbol{x}_0$が畳み込みネットワークを通過することを考える。 ネットワークは$L$層含む場合、第$l$層での非線形変換(Batch Normalization,ReLU,Pooling,Convolutionなどを組み合わせたもの)を$H_l(・)$、 第$l$層の出力を$\boldsymbol{x}_l$と表す。 Dense connectivity(dense block) 第$l$層でその前のすべての出力$\boldsymbol{x}0,\cdots,\boldsymbol{x}{l-1}$を受け取る。 \boldsymbol{x}_l=H_l([\boldsymbol{x}_0,\cdots,\boldsymbol{x}_{l-1}]) ここで、$[\boldsymbol{x}0,\cdots,\boldsymbol{x}{l-1}]$は第0~$l-1$層での出力を結合したものである。 composite function Batch Normalization(BN)、ReLU、3×3畳み込み(Conv)を組み合わせて使用する。 Pooilng layers(transition layer) 特徴マップのダウンサンプリングを行うため、 dense blockの間に、畳み込みとプーリングを行うtransition layerを挿入します。 この層はBN、1×1Conv、2×2Pooligで構成されます。 入力のチャンネル数が$m$のとき、transition layerでの出力を$m\theta$とします。 ここで、$0<\theta\leq1$とし、$0<\theta<1$のときDenseNet-Cと呼びここでは$\theta=0.5$とします。 Growth rate $H_l$が$k$個の特徴マップを作り出す場合、第$l$層への入力は$k_0+k(l-1)$チャンネルとなります。ここで、$k_0$は入力層のチャンネル数である。 この$k$をgrowth rateと呼ぶ。 bottleneck layers dense blockは$k$チャンネルの特徴マップを出力するが、これは入力と比べて少ない。 dense blockの3×3Convの前に1×1Convを追加して(bottleneck)、3×3Convへの入力チャンネル数を減らして計算効率を改善する。 bottleneck layersの構造は、BN-ReLU-1×1Conv-BN-ReLU-3×3Convとなり、DenseNet-Bと表します。 今回は1×1Convで$4×k$個の特徴マップを作ることとする。 Implementation Details 最初のdense blockに入力する前に、入力を16チャンネル(ConvNet-BCの場合は、growth rateの2倍)に変換します。 今回は、strideが2の7×7Convと3×3のmax poolingを使用します。 最後のdense blockの後にはGlobal Avarage Poolng、そしてsoftmaxを使用して出力を出します。 学習 SGDで学習し、学習率は0.1とする。エポックが全体のエポック数の50%、75%となったら0.1を掛けて減衰させる。 weight decayは0.0001とし、Nesterov momentumを0.9とする。 rateを0.2としたdropout層を各畳み込み層の後に入れる。 実装 $k=32$とし、DenseNet-169を実装します。使用する構造はbottleneck構造を持ったDenseNet-BCです。 keras 必要なライブラリのインポートを行います。 import tensorflow.keras as keras import tensorflow as tf from tensorflow.keras.models import Sequential, Model from tensorflow.keras.layers import Input, Conv2D, Activation, MaxPooling2D, AveragePooling2D, Flatten, Dense, Dropout, GlobalAveragePooling2D, BatchNormalization, Add from keras.layers.merge import concatenate from tensorflow.keras import backend as K from tensorflow.keras.optimizers import SGD from tensorflow.keras.callbacks import LearningRateScheduler from keras.datasets import cifar10 import numpy as np import cv2 まず、BN-ReLU-Convのお決まりの流れを定義しておきます。 class bn_relu_conv(Model): def __init__(self, out_channels, kernel_size=1, strides=1, name=None): super(bn_relu_conv, self).__init__(name=name) self.bn = BatchNormalization() self.relu = Activation("relu") self.conv = Conv2D(out_channels, kernel_size=kernel_size, strides=strides, padding='same') def call(self, x): x = self.bn(x) x = self.relu(x) x = self.conv(x) return x Dense Layerの定義です。 2回の畳み込み処理を行います。 class dense_layer(Model): """dense block""" def __init__(self, growth_rate, n): super(dense_layer, self).__init__(name='layer'+'_'+str(n)) self.conv1 = bn_relu_conv(out_channels=growth_rate*4, kernel_size=1, strides=1, name='layer'+'_'+str(n)+'conv1') self.conv2 = bn_relu_conv(out_channels=growth_rate, kernel_size=3, strides=1, name='layer'+'_'+str(n)+'conv2') def call(self, x): out = self.conv1(x) out = self.conv2(out) return out Dense Blockの定義です。 Dense Layerを繰り返し、各Layerの出力を最後に結合します。 class dense_block(Model): """dense block""" def __init__(self, num_layers, growth_rate, name): super(dense_block, self).__init__(name=name) self.dense_layers = [] for i in range(num_layers): self.dense_layers += [dense_layer(growth_rate=growth_rate, n=i)] def call(self, x): for layer in self.dense_layers: main = x x = layer(x) x = concatenate([main, x]) return x transition Layerの定義です。 畳み込みとプーリングを行い、特徴マップを変形します。 class transition_layer(Model): def __init__(self, input_channels, name): super(transition_layer, self).__init__(name=name) self.conv = bn_relu_conv(out_channels=input_channels, kernel_size=1, strides=1, name=name+'conv1') self.pool = AveragePooling2D((2, 2)) def call(self, x): out = self.conv(x) out = self.pool(out) return out DenseNetの本体の実装を行います。 class dense_net(Model): """dense block""" def __init__(self, num_layers, growth_rate, num_classes=1000): super(dense_net, self).__init__() self.layer_dense = [] in_channels= 2 * growth_rate self.layer_dense += [bn_relu_conv(out_channels=in_channels, kernel_size=7, strides=2, name='conv1'), MaxPooling2D((3,3), strides=2, padding='same')] for i in range(len(num_layers)): self.layer_dense += [dense_block(num_layers=num_layers[i], growth_rate=growth_rate, name='block_'+str(i))] in_channels += growth_rate * num_layers[i] if i != (len(num_layers)-1): self.layer_dense += [transition_layer(in_channels//2, name='trans_'+str(i))] in_channels = in_channels//2 self.layer_dense += [GlobalAveragePooling2D(), Dense(num_classes, activation="softmax")] def call(self, x): for layer in self.layer_dense: x = layer(x) return x 構造の確認をします。 model = dense_net(num_layers=[6,12,32,32], growth_rate=32) model.build((None, 224, 224, 3)) # build with input shape. dummy_input = Input(shape=(224, 224, 3)) # declare without batch demension. model_summary = Model(inputs=[dummy_input], outputs=model.call(dummy_input), name="pretrained") model_summary.summary() Model: "pretrained" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 224, 224, 3)] 0 _________________________________________________________________ conv1 (bn_relu_conv) (None, 112, 112, 64) 9484 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 56, 56, 64) 0 _________________________________________________________________ block_0 (dense_block) (None, 56, 56, 256) 339264 _________________________________________________________________ trans_0 (transition_layer) (None, 28, 28, 128) 33920 _________________________________________________________________ block_1 (dense_block) (None, 28, 28, 512) 931968 _________________________________________________________________ trans_1 (transition_layer) (None, 14, 14, 256) 133376 _________________________________________________________________ block_2 (dense_block) (None, 14, 14, 1280) 4377600 _________________________________________________________________ trans_2 (transition_layer) (None, 7, 7, 640) 824960 _________________________________________________________________ block_3 (dense_block) (None, 7, 7, 1664) 5999616 _________________________________________________________________ global_average_pooling2d (Gl (None, 1664) 0 _________________________________________________________________ dense (Dense) (None, 1000) 1665000 ================================================================= Total params: 14,315,188 Trainable params: 14,160,238 Non-trainable params: 154,950 _________________________________________________________________ 学習の設定を行い、実行はせず実装を終わりにします。 # 学習率を返す関数を用意する def lr_schedul(epoch): x = 0.1 if epoch >= 20: x = 0.1*0.1 if epoch >= 30: x = 0.1*(0.1**2) return x lr_decay = LearningRateScheduler( lr_schedul, verbose=1, ) sgd = SGD(lr=0.1, momentum=0.9, decay=1e-4, nesterov=True) model.compile(loss=['categorical_crossentropy'], optimizer=sgd, metrics=['accuracy']) batch_size = 64 epochs = 40 history=model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_data=(x_test, y_test), callbacks=[lr_decay]) 転移学習(keras) 学習済みのモデルが提供されています。 出力層を付け加えて使用します。 from keras.applications.densenet import DenseNet169 base_model = DenseNet169(weights='imagenet', include_top=False, classes=1000) x = base_model.output x = GlobalAveragePooling2D()(x) x = Dense(1024, activation='relu')(x) output = Dense(10, activation='softmax')(x) model = Model(inputs=base_model.input, outputs=output) for layer in base_model.layers: layer.trainable = False # 学習率を返す関数を用意する def lr_schedul(epoch): x = 0.1 if epoch >= 20: x = 0.1*0.1 if epoch >= 30: x = 0.1*(0.1**2) return x lr_decay = LearningRateScheduler( lr_schedul, verbose=1, ) sgd = SGD(lr=0.1, momentum=0.9, decay=1e-4, nesterov=True) model.compile(loss=['categorical_crossentropy'], optimizer=sgd, metrics=['accuracy']) history=model.fit(x_train, y_train, batch_size=batch_size, epochs=epochs, validation_data=(x_test, y_test), callbacks=[lr_decay]) pytorch 実装の内容はkerasとほぼ同様です。 必要なライブラリのインポートを行います。 import torch import torch.nn as nn import torch.optim as optim from torchsummary import summary import pytorch_lightning as pl from torchmetrics import Accuracy as accuracy 各モジュールの定義を行います。 class bn_relu_conv(nn.Module): def __init__(self, in_channels, out_channels, kernel_size, stride,padding=1): super(bn_relu_conv, self).__init__() self.bn = nn.BatchNorm2d(in_channels) self.relu = nn.ReLU(inplace=True) self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding) self.drop = nn.Dropout(0.2) def forward(self, x): out = self.bn(x) out = self.relu(out) out = self.conv(out) out = self.drop(out) return out class dense_layer(nn.Module): def __init__(self, in_channels, growth_rate): super(dense_layer, self).__init__() self.conv1 = bn_relu_conv(in_channels, out_channels=growth_rate*4, kernel_size=1, stride=1, padding=0) self.conv2 = bn_relu_conv(growth_rate*4, out_channels=growth_rate, kernel_size=3, stride=1) def forward(self, x): out = self.conv1(x) out = self.conv2(out) return out class dense_block(nn.Module): """dense block""" def __init__(self, in_channels, num_layers, growth_rate): super(dense_block, self).__init__() dense_layers = [] for i in range(num_layers): dense_layers += [dense_layer(in_channels=int(in_channels + i*growth_rate), growth_rate=growth_rate)] self.dense_layers = nn.Sequential(*dense_layers) def forward(self, x): for layer in self.dense_layers: out = layer(x) x = torch.cat([x, out], 1) return x class transition_layer(nn.Module): def __init__(self, in_channels, out_channels): super(transition_layer, self).__init__() self.conv = bn_relu_conv(in_channels=in_channels, out_channels=out_channels, kernel_size=1, stride=1,padding=0) self.pool = nn.AvgPool2d(kernel_size=2, stride=2) def forward(self, x): out = self.conv(x) out = self.pool(out) return out DenseNet本体の実装です。 class dense_net(nn.Module): """dense block""" def __init__(self, num_layers, growth_rate, num_classes=1000): super(dense_net, self).__init__() in_channels= 2 * growth_rate self.first_layer = nn.Sequential(*[bn_relu_conv(in_channels=3, out_channels=in_channels, kernel_size=7, stride=2, padding=3), nn.MaxPool2d(kernel_size=3, stride=2, padding=1)]) layer_dense = [] for i in range(len(num_layers)): layer_dense += [dense_block(in_channels=in_channels, num_layers=num_layers[i], growth_rate=growth_rate)] in_channels += growth_rate * num_layers[i] if i != (len(num_layers)-1): layer_dense += [transition_layer(in_channels=in_channels, out_channels=in_channels//2)] in_channels = in_channels//2 layer_dense += [nn.AdaptiveAvgPool2d((1,1))] self.layer_dense = nn.Sequential(*layer_dense) self.classifier = nn.Linear(in_channels, num_classes) def forward(self, x): out = self.first_layer(x) out = self.layer_dense(out) out = out.view(out.shape[0], -1) out = self.classifier(out) return x モデルの構造を確認します。 summary(dense_net(num_layers=[6,12,32,32], growth_rate=32), (3,224,224)) ---------------------------------------------------------------- Layer (type) Output Shape Param # ================================================================ BatchNorm2d-1 [-1, 3, 224, 224] 6 ReLU-2 [-1, 3, 224, 224] 0 Conv2d-3 [-1, 64, 112, 112] 9,472 Dropout-4 [-1, 64, 112, 112] 0 中略 Conv2d-929 [-1, 32, 7, 7] 36,896 Dropout-930 [-1, 32, 7, 7] 0 bn_relu_conv-931 [-1, 32, 7, 7] 0 dense_layer-932 [-1, 32, 7, 7] 0 dense_block-933 [-1, 1664, 7, 7] 0 AdaptiveAvgPool2d-934 [-1, 1664, 1, 1] 0 Linear-935 [-1, 1000] 1,665,000 ================================================================ Total params: 14,160,238 Trainable params: 14,160,238 Non-trainable params: 0 ---------------------------------------------------------------- Input size (MB): 0.57 Forward/backward pass size (MB): 471.02 Params size (MB): 54.02 Estimated Total Size (MB): 525.61 ---------------------------------------------------------------- 学習の設定を行い、実装を終わりにします。 class DenseTrainer(pl.LightningModule): def __init__(self): super().__init__() self.model = dense_net(num_layers=[6,12,32,32], growth_rate=32) def forward(self, x): x = self.model(x) return x def training_step(self, batch, batch_idx): x, y = batch #x, y = x.to(device), y.to(device) y_hat = self.forward(x) loss = nn.CrossEntropyLoss()(y_hat, y) return {'loss': loss, 'y_hat':y_hat, 'y':y, 'batch_loss': loss.item()*x.size(0)} def validation_step(self, batch, batch_idx): x, y = batch #x, y = x.to(device), y.to(device) y_hat = self.forward(x) loss = nn.CrossEntropyLoss()(y_hat, y) return {'y_hat':y_hat, 'y':y, 'batch_loss': loss.item()*x.size(0)} def test_step(self, batch, batch_nb): x, y = batch #x, y = x.to(device), y.to(device) y_hat = self.forward(x) loss = nn.CrossEntropyLoss()(y_hat, y) y_label = torch.argmax(y_hat, dim=1) acc = accuracy()(y_label, y) return {'test_loss': loss, 'test_acc': acc} def training_epoch_end(self, train_step_output): y_hat = torch.cat([val['y_hat'] for val in train_step_outputs], dim=0) y = torch.cat([val['y'] for val in train_step_outputs], dim=0) epoch_loss = sum([val['batch_loss'] for val in train_step_outputs]) / y_hat.size(0) preds = torch.argmax(y_hat, dim=1) acc = accuracy()(preds, y) self.log('train_loss', epoch_loss, prog_bar=True, on_epoch=True) self.log('train_acc', acc, prog_bar=True, on_epoch=True) print('---------- Current Epoch {} ----------'.format(self.current_epoch + 1)) print('train Loss: {:.4f} train Acc: {:.4f}'.format(epoch_loass, acc)) def validation_epoch_end(self, val_step_outputs): y_hat = torch.cat([val['y_hat'] for val in val_step_outputs], dim=0) y = torch.cat([val['y'] for val in val_step_outputs], dim=0) epoch_loss = sum([val['batch_loss'] for val in val_step_outputs]) / y_hat.size(0) preds = torch.argmax(y_hat, dim=1) acc = accuracy()(preds, y) self.log('val_loss', epoch_loss, prog_bar=True, on_epoch=True) self.log('val_acc', acc, prog_bar=True, on_epoch=True) print('valid Loss: {:.4f} valid Acc: {:.4f}'.format(epoch_loss, acc)) # New: テストデータに対するエポックごとの処理 def test_epoch_end(self, test_step_outputs): y_hat = torch.cat([val['y_hat'] for val in test_step_outputs], dim=0) y = torch.cat([val['y'] for val in test_step_outputs], dim=0) epoch_loss = sum([val['batch_loss'] for val in test_step_outputs]) / y_hat.size(0) preds = torch.argmax(y_hat, dim=1) acc = accuracy()(preds, y) self.log('test_loss', epoch_loss, prog_bar=True, on_epoch=True) self.log('test_acc', acc, prog_bar=True, on_epoch=True) print('test Loss: {:.4f} test Acc: {:.4f}'.format(epoch_loss, acc)) def configure_optimizers(self): optimizer = optim.SGD(self.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[150, 225], gamma=0.1) return {'optimizer': optimizer, 'lr_scheduler': scheduler} 転移学習(pytorch) pytorchでも学習済のモデルが提供されています。 from torchvision import models dense = models.densenet161(pretrained=True) class DenseTrainer(pl.LightningModule): def __init__(self): super(VGGTrainer, self).__init__() dense = models.densenet161(pretrained=True) dense.classifier = nn.Linear(in_features=2208, out_features=10) self.model = dense update_param_names = ['classifier.weight', 'classifier.bias'] for name, param in self.model.named_parameters(): if name in update_param_names: param.requires_grad = True else: param.requires_grad = False def forward(self, x): x = self.model(x) return x def training_step(self, batch, batch_idx): x, y = batch #x, y = x.to(device), y.to(device) y_hat = self.forward(x) loss = nn.CrossEntropyLoss()(y_hat, y) return {'loss': loss, 'y_hat':y_hat, 'y':y, 'batch_loss': loss.item()*x.size(0)} def validation_step(self, batch, batch_idx): x, y = batch #x, y = x.to(device), y.to(device) y_hat = self.forward(x) loss = nn.CrossEntropyLoss()(y_hat, y) return {'y_hat':y_hat, 'y':y, 'batch_loss': loss.item()*x.size(0)} def test_step(self, batch, batch_nb): x, y = batch #x, y = x.to(device), y.to(device) y_hat = self.forward(x) loss = nn.CrossEntropyLoss()(y_hat, y) y_label = torch.argmax(y_hat, dim=1) acc = accuracy()(y_label, y) return {'test_loss': loss, 'test_acc': acc} def training_epoch_end(self, train_step_output): y_hat = torch.cat([val['y_hat'] for val in train_step_outputs], dim=0) y = torch.cat([val['y'] for val in train_step_outputs], dim=0) epoch_loss = sum([val['batch_loss'] for val in train_step_outputs]) / y_hat.size(0) preds = torch.argmax(y_hat, dim=1) acc = accuracy()(preds, y) self.log('train_loss', epoch_loss, prog_bar=True, on_epoch=True) self.log('train_acc', acc, prog_bar=True, on_epoch=True) print('---------- Current Epoch {} ----------'.format(self.current_epoch + 1)) print('train Loss: {:.4f} train Acc: {:.4f}'.format(epoch_loass, acc)) def validation_epoch_end(self, val_step_outputs): y_hat = torch.cat([val['y_hat'] for val in val_step_outputs], dim=0) y = torch.cat([val['y'] for val in val_step_outputs], dim=0) epoch_loss = sum([val['batch_loss'] for val in val_step_outputs]) / y_hat.size(0) preds = torch.argmax(y_hat, dim=1) acc = accuracy()(preds, y) self.log('val_loss', epoch_loss, prog_bar=True, on_epoch=True) self.log('val_acc', acc, prog_bar=True, on_epoch=True) print('valid Loss: {:.4f} valid Acc: {:.4f}'.format(epoch_loss, acc)) # New: テストデータに対するエポックごとの処理 def test_epoch_end(self, test_step_outputs): y_hat = torch.cat([val['y_hat'] for val in test_step_outputs], dim=0) y = torch.cat([val['y'] for val in test_step_outputs], dim=0) epoch_loss = sum([val['batch_loss'] for val in test_step_outputs]) / y_hat.size(0) preds = torch.argmax(y_hat, dim=1) acc = accuracy()(preds, y) self.log('test_loss', epoch_loss, prog_bar=True, on_epoch=True) self.log('test_acc', acc, prog_bar=True, on_epoch=True) print('test Loss: {:.4f} test Acc: {:.4f}'.format(epoch_loss, acc)) def configure_optimizers(self): optimizer = optim.SGD(self.parameters(), lr=0.1, momentum=0.9, weight_decay=1e-4) scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[20, 30], gamma=0.1) return {'optimizer': optimizer, 'lr_scheduler': scheduler} これでDenseNet論文のメモ書きを終わります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ネット用にstylegan2を使って自分に似た実在しない顔を作る

はじめに ネット用に顔写真を載せなきゃいけなくなったけど、ネットに個人情報なんて載せたくないということがありますよね。 そこで学習済みのstylegan2を使って自分の画像に近い存在しない画像を簡単に生成できるようにしたものをgoogle colablatoryに公開しました。 google colablatoryとはgoogleが提供するもので、googleアカウントさえあれば無料でブラウザ上でGPUを使える神サービスです。 作ったものがこちらになります。 どんな画像が作れるの? 上から 元画像 元画像を切り取ったもの 生成された存在しない顔 となっています。 stylegan2のすごいところは1024x1024の高画質画像を生成できるところです。 また、おまけでgif動画も作れます。 元となったコード このコードはstylegan2を試すものであり、 cedro様(@jun40vn)の「GANの潜在空間に新垣結衣は住んでいるのか?」 の記事内の「Search_for_Yui.ipynb」 をベースとして、自分の写真で実行しやすいように改良したものです。 @jun40vn 様の公開されたColab Notebooksがなければ作れませんした。心より感謝申し上げます。 改良点ついては適宜Colab Notebooks内で説明していますが、重要な部分は、 画像をアップロードできるようにした 探索回数を指定できるようにした 探索中の画像の保存回数を指定できるようにした。 です。 また写真はすべて フリー素材ぱくたそ( www.pakutaso.com ) から使用しています。 使い方 1.まず公開したColab Notebooksにアクセスします。(googleアカウントにログインしてください。) 2.実行する 上部の「ランタイム」→「すべてのセルを実行」をクリック 3.画像をアップロードする 画像のアップロードという項に、以下のような「ファイルの選択」が現れます。 なので生成したい画像の素となる画像をアップロードしてください。 このようにドラックアンドドロップすることもできます。 4.探索を待つ 探索に一番時間がかかります。 気長に待ってください。 5.画像の表示 学習が終わったら順次画像が表示されていきます。 元画像と最終的な結果の表示 上から 元画像 元画像を切り取ったもの 生成された存在しない顔 となっています。 探索中の生成画像の変化 ここで、左に行くほど初期の生成画像となっています。 一番右は元となった画像なので注意してください。 高画質画像 1024x1024の高画質画像が表示されます。 右クリックで保存できます。 6.おまけ パラメータの変化 探索回数をnum_stepsで変更できます。(デフォルトで300) 大きくするとよりアップロードした画像に近づきますが、実行時間が長くなります。 探索中の画像の表示回数はnumber_snapshotsで変更できます。(デフォルトで5) 大きくしてもあまり実行時間が変わったりしませんが、大きすぎると割り当てられたRAMやディスクを使い果たしてしまうかもしれません。 GPU 使用するGPUについては、Colab Notebooksの上のほうに表示されています。 詳しくは「google colab GPU ガチャ」で検索 Tesla T4がおそらく当たりです。 gif 複数枚アップロードした場合は、順番に変化していくgif動画を作れます。 注意 表示範囲 今回のプログラムでは、目の位置が中心になるように画像を切り取っています。(うろ覚え、詳しくは元となった記事を読んでください。) そのため、場合によっては頭の上が切れてしまうことがあります 複数人 複数人写っていると人数分切り取られます。 しかし私の改良したプログラムでは、アップロードした枚数分しか学習やら表示やらをしません。 また顔でない部分が顔と認識されてしまうこともあります。 そこらへんはうまくやってください。 顔が認識されない 顔が認識されないこともありますがどうしようもありません。 他の画像を使用してください。 まとめ 写真に近い存在しない画像を生成する方法を説明しました。 ぜひ試してみてください。 そして改めて、@jun40vn 様の公開されたColab Notebooksがなければ作れませんした。心より感謝申し上げます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ネット用にstylegan2を使って自分に似た実在しない顔画像を作る

はじめに ネット用に顔写真を載せなきゃいけなくなったけど、ネットに個人情報なんて載せたくないということがありますよね。 そこで学習済みのstylegan2を使って自分の画像に近い存在しない画像を簡単に生成できるようにしたものをgoogle colablatoryに公開しました。 google colablatoryとはgoogleが提供するもので、googleアカウントさえあれば無料でブラウザ上でGPUを使える神サービスです。 作ったものがこちらになります。 どんな画像が作れるの? 上から 元画像 元画像を切り取ったもの 生成された存在しない顔 となっています。 stylegan2のすごいところは1024x1024の高画質画像を生成できるところです。 また、おまけでgif動画も作れます。 元となったコード このコードはstylegan2を試すものであり、 cedro様(@jun40vn)の「GANの潜在空間に新垣結衣は住んでいるのか?」 の記事内の「Search_for_Yui.ipynb」 をベースとして、自分の写真で実行しやすいように改良したものです。 @jun40vn 様の公開されたColab Notebooksがなければ作れませんした。心より感謝申し上げます。 改良点ついては適宜Colab Notebooks内で説明していますが、重要な部分は、 画像をアップロードできるようにした 探索回数を指定できるようにした 探索中の画像の保存回数を指定できるようにした。 です。 また写真はすべて フリー素材ぱくたそ( www.pakutaso.com ) から使用しています。 使い方 1.まず公開したColab Notebooksにアクセスします。(googleアカウントにログインしてください。) 2.実行する 上部の「ランタイム」→「すべてのセルを実行」をクリック 3.画像をアップロードする 画像のアップロードという項に、以下のような「ファイルの選択」が現れます。 なので生成したい画像の素となる画像をアップロードしてください。 このようにドラックアンドドロップすることもできます。 4.探索を待つ 探索に一番時間がかかります。 気長に待ってください。 5.画像の表示 学習が終わったら順次画像が表示されていきます。 元画像と最終的な結果の表示 上から 元画像 元画像を切り取ったもの 生成された存在しない顔 となっています。 探索中の生成画像の変化 ここで、左に行くほど初期の生成画像となっています。 一番右は元となった画像なので注意してください。 高画質画像 1024x1024の高画質画像が表示されます。 右クリックで保存できます。 6.おまけ パラメータの変化 探索回数をnum_stepsで変更できます。(デフォルトで300) 大きくするとよりアップロードした画像に近づきますが、実行時間が長くなります。 探索中の画像の表示回数はnumber_snapshotsで変更できます。(デフォルトで5) 大きくしてもあまり実行時間が変わったりしませんが、大きすぎると割り当てられたRAMやディスクを使い果たしてしまうかもしれません。 GPU 使用するGPUについては、Colab Notebooksの上のほうに表示されています。 詳しくは「google colab GPU ガチャ」で検索 Tesla T4がおそらく当たりです。 gif 複数枚アップロードした場合は、順番に変化していくgif動画を作れます。 注意 表示範囲 今回のプログラムでは、目の位置が中心になるように画像を切り取っています。(うろ覚え、詳しくは元となった記事を読んでください。) そのため、場合によっては頭の上が切れてしまうことがあります 複数人 複数人写っていると人数分切り取られます。 しかし私の改良したプログラムでは、アップロードした枚数分しか学習やら表示やらをしません。 また顔でない部分が顔と認識されてしまうこともあります。 そこらへんはうまくやってください。 顔が認識されない 顔が認識されないこともありますがどうしようもありません。 他の画像を使用してください。 まとめ 写真に近い存在しない画像を生成する方法を説明しました。 ぜひ試してみてください。 そして改めて、@jun40vn 様の公開されたColab Notebooksがなければ作れませんした。心より感謝申し上げます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

メモ:Pytonのテストフレームワークpytestの基本

Pythonは軽く触ってはいたが、本番プロダクトでの利用は殆どなかったのでテストフレームワークを使っていなかった。 pytestというテストフレームワークがPythonには存在しているので見ておく。 基本的にはdefで定義されるメソッドに対して、引数を与えて正しい結果が返ってくるか、 を実行して確認するための単体テストツール。 準備 pip install pytest でインストール可能 mainフォルダとtestsフォルダにわけてソースコードを管理して、 testsフォルダ内のtest_xxx.pyのtest_xxxメソッドを順次実行していくので、各フォルダに配置する。 なお、アドオンだと pytest-covでcoverage(テスト網羅率)を取れる。 toxを使うと仮想のテスト環境を設定出来るので、バージョン依存を知りたい時等には使える。 pytest-mockでモック テスト記述方法 基本的にはtest_メソッドを用意すればいいのだが、 インスタンスをテストメソッド内で作成してテストしても良い。 def test_action(self,x): o = cls() assert o.action(2) == 1 エラーがraiseされるのが正しい処理の場合はpytest.raisesを利用する。 def test_action(self): with pytest.raises(ValueError): o = cls() o.action('2') 何かをテスト前に実行したい場合はfixtureを利用。こことか参照 @pytest.fixture def init_xxx(self): xxx 実行 pytest コマンドで流せばテストが実行されて 8 passed in 0.06s のように表示される。 ImportError: cannot import name ... のようにエラーが出る場合はinit.py(空ファイル)をmainやtestsに配置すればOK その他 時間がかかる処理を特定するには --durations=10 のようにオプション指定すると上位10個の遅い処理が出てくる。 あと、少し前の情報だと、VSCodeの設定は linting.flake8.enabled、mypy.enabled,enabled false が良いらしい。 なお、AWSのCodeBuildで自動テストを実行する場合は以下を参考にする https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/test-report-pytest.html https://dev.classmethod.jp/articles/codebuild-test-reporting-ga/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

2万クラスの物体検出を使うと、かゆいところに手が届き過ぎる

超高精度の物体検出とセグメンテーション機械学習モデルを使う方法です PythonでDeticという機械学習モデルを使って、細かく検出します。 従来の物体検出では検出できない、密集したものを一つ一つ検出して仕分けしたい FaceBookReseachの最新モデルがすごい 2022年に発表されたDeticというモデルは2万分類ができ、高精度。 使用方法 公式のColabで推論ができる。 基本的には、リポジトリをクローンして、必要なモジュールをインストールして、構成ファイルと重みからPredictorを初期化して、画像を入力するだけである。 predictor = DefaultPredictor(cfg) outputs = predictor(im) アプリケーションも進化する これだけのモデルが無料で使えるのだから、アプリケーションもどんどん進化しそうである。 ? フリーランスエンジニアです。 お仕事のご相談こちらまで rockyshikoku@gmail.com Core MLやARKitを使ったアプリを作っています。 機械学習/AR関連の情報を発信しています。 Twitter Medium
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonの実行モード

pythonの実行モードには以下の二つがある インタラクティブ・モード スクリプト・モード <定義> インタラクティブ・モードとは、コマンド・プロンプト上で直接プログラム・コードを入力しながら、プログラムを1行1行の対話形式で実行する方法だ。一方のスクリプト・モードとは、一連のプログラム・コードを書き込んだスクリプト・ファイル(.pyファイル)を作成しておき、そのスクリプト・ファイルを一気に実行する方法である。 <自分なりの定義> インタラクティブ・モード:双方向 スクリプト・モード   :一方向
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[py2rb] 仮引数の既定値

はじめに 移植やってます。 ( from python 3.7 to ruby 2.7 ) 仮引数 (Python) def func(a, b=1, c=2, **kwargs): print(a, b, c) func(0, 4, 5) func(0, d=4, c=5) # 0 4 5 # 0 1 5 d=4が実引数の2番目にありますが、辞書扱いとなり仮引数の2番目のbには格納されないようです。 独習Python 330p 仮引数 失敗 (Ruby) def func(a, b=1, c=2, **kwargs) p [a, b, c] end func(0, 4, 5) func(0, d=4, c=5) # [0, 4, 5] # [0, 4, 5] d=4の戻り値である4がbに格納されます。 仮引数 成功 (Ruby) def func(a, b: 1, c: 2, **kwargs) p [a, b, c] end func(0, b: 4, c: 5) func(0, d: 4, c: 5) # [0, 4, 5] # [0, 1, 5] 独習Ruby 387p プロを目指す人のためのRuby入門[改訂2版] 186p 仮引数 成功2 (Ruby) def func(a, b: 1, c: 2, **kwargs) p [a, b, c] end func(0, b: 4, c: 5) func(0, 'd' => 4, c: 5) # [0, 4, 5] # [0, 1, 5] このほうが紛らわしくないかも。 メモ Python の 仮引数の既定値 を学習した 百里を行く者は九十里を半ばとす
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

カレンダーを表示⇒日付をクリックすると入力フォームに自動入力される【Django】

カレンダーを表示⇒日付選択で入力フォームに反映 カレンダーボタンから、カレンダーを表示し日付をクリックできます。 その後選択した日付が入力フォームに自動で反映される設定です。 ※めちゃくちゃ簡素に書いてますmm フォーム(forms.py)の設定 forms.py from django import forms #(中略) class EnqueteForm(forms.Form): birth = forms.DateField( label='生年月日', widget=forms.DateInput(format='%Y-%m-%d', attrs={'type': 'date'}), # required=False ) #表示するための記述 def __init__(self,*args,**kwargs): super().__init__(*args,**kwargs) self.fields['birth'].widget.attrs['class']='form-control col-11' Tabがこちらで反映されてない部分がありますので、適宜修正をお願いします。 ビュー(views.py)の設定 views.py from .forms import EnqueteForm #(中略) class EnqueteView(generic.FormView): template_name = "enquete.html" form_class = EnqueteForm #forms.pyで作ったクラス名 success_url = reverse_lazy('information:enquete') JavaScriptの設定(base.html) base.html <script> $(function () { // 日付は、年-月-日 の形式でお願いする。 let dateFormat = 'yy-mm-dd'; $('#id_created_at').datepicker({ dateFormat: dateFormat }); }); // drag and drop event $(document).ready(function() { var obj = $("#dragandrophandler"); obj.on('dragenter', function (e) { e.stopPropagation(); e.preventDefault(); $(this).css('border', '2px solid #0B85A1'); }); obj.on('dragover', function (e) { e.stopPropagation(); e.preventDefault(); }); obj.on('drop', function(e) { $(this).css('border', '2px dotted #0B85A1'); e.preventDefault(); var files = e.originalEvent.dataTransfer.files; // Modal dialog popupImage(file, obj); }); // Avoid opening in a browser if the file is dropped outside the div $(document).on('dragenter', function (e) { e.stopPropagation(); e.preventDefault(); }); $(document).on('dragover', function (e) { e.stopPropagation(); e.preventDefault(); obj.css('border', '2px dotted #0B85A1'); }); $(document).on('drop', function (e) { e.stopPropagation(); e.preventDefault(); }); }); </script> 今回もJS部分はhtmlにべた書きしましたが、他にいい方法があったらご教示いただけますと幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python で Azure Activity-Log を取得してみました

概要 Azure SDK for Python を利用して、とある LogAnalyticsワークスペース に保存されている Activity-Log を、クエリ処理してログを抽出する Python プログラムです。 実行環境 macOS Monterey 12.1 python 3.8.3 前提条件 Activity-Log が LogAnalyticsワークスペースに宛先設定れており、そのワークスペースIDを取得できていること 実行プログラム GetWorkspaceLogsQuery.py import time import argparse import pandas as pd from azure.core.exceptions import HttpResponseError from azure.identity import AzureCliCredential, DefaultAzureCredential from azure.monitor.query import LogsQueryClient, LogsQueryStatus from datetime import datetime # LogAnalyticsワークスペースIDの定義 LOG_WORKSPACE_ID = 'xxxxxxxx-0033-4040-9090-zzzzzzzzzzzz' # LogAnalyticsワークスペースからLogsQueryを操作するオブジェクトの取得 def GetLogsQueryClient(): logs_client = LogsQueryClient( credential=AzureCliCredential() # credential=DefaultAzureCredential() ) return logs_client # LogAnalyticsワークスペースから指定するリソースグループのActivityログを取得 def GetWorkspaceLogsQuery(rg_name, start_date, end_date): print("\n\n ##### From : {} To : {} #####".format(start_date, end_date)) # クエリ文の定義(特定のリソースグループを指定) # query_str = "AzureActivity | where ResourceGroup == '{}' | take 1".format(str.upper(rg_name)) query_str = "AzureActivity | where ResourceGroup == '{}'".format(str.upper(rg_name)) print(query_str) # LogsQueryを操作するオブジェクトの取得 logs_client = GetLogsQueryClient() try: response = logs_client.query_workspace( workspace_id=LOG_WORKSPACE_ID, query=query_str, timespan=(start_date, end_date) ) if response.status == LogsQueryStatus.PARTIAL: error = response.partial_error data = response.partial_data print(error.message) elif response.status == LogsQueryStatus.SUCCESS: data = response.tables for table in data: df = pd.DataFrame(data=table.rows, columns=table.columns) with pd.option_context('display.max_rows', None, 'display.max_columns', None, 'display.max_colwidth', -1): print(df) except HttpResponseError as err: print("Something Fatal Happened") print (err) logs_client.close() return df.ResourceGroup.count() # パラメータの文字列を日付型に変換 def valid_date(s): try: return datetime.strptime(s, "%Y-%m-%d") except ValueError: msg = "Not a valid date: '{0}'.".format(s) raise argparse.ArgumentTypeError(msg) # メイン if __name__ == '__main__': parser = argparse.ArgumentParser(description='LogAnalyticsワークスペースから指定するリソースグループのActivityログを取得する') parser.add_argument('-g', '--rg', type=str, help='リソースグループ名', required=True) parser.add_argument('-s', '--startdate', type=valid_date, help='From日付 yyyy-mm-dd', required=True) parser.add_argument('-e', '--enddate', type=valid_date, help='To日付 yyyy-mm-dd', required=True) args = parser.parse_args() start = time.time() cnt = GetWorkspaceLogsQuery(args.rg, args.startdate, args.enddate) generate_time = time.time() - start print("\n Activityログ数 : " + str(cnt)) print("\n 取得時間:{0}".format(generate_time) + " [sec] \n") プログラムのHELP表示 ## HELPの表示 $ python GetWorkspaceLogsQuery.py -h usage: GetWorkspaceLogsQuery.py [-h] -g RG -s STARTDATE -e ENDDATE LogAnalyticsワークスペースから指定するリソースグループのActivityログを取得する optional arguments: -h, --help show this help message and exit -g RG, --rg RG リソースグループ名 -s STARTDATE, --startdate STARTDATE From日付 yyyy-mm-dd -e ENDDATE, --enddate ENDDATE To日付 yyyy-mm-dd プログラムの実行 ## 実行状況 $ python GetWorkspaceLogsQuery.py -g rg_ituru_bricks01 -s 2021-11-11 -e 2022-01-11 ##### From : 2021-11-11 00:00:00 To : 2022-01-11 00:00:00 ##### AzureActivity | where ResourceGroup == 'RG_ITURU_BRICKS01' : 省略 : Activityログ数 : 59 取得時間:1.656360149383545 [sec] まとめ 上記の方法で Activityログ を取得し、あとは、煮るなり焼くなり、、、してみたいです、、、 参考記事 以下の記事を参考にさせていただきました。感謝申し上げます。 Azure Monitor Query client library for Python AzureActivity
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像処理】NumpyでDoGフィルター

NumpyでDoG(Diffrence of Gaussian)フィルターを実装してみます。 DoGフィルターでは、$\sigma$の値が異なる2つのガウシアンフィルターをそれぞれ適用し、その差分を求めます。 まず、DoGフィルターを適用する画像を読み込んでグレースケール画像にします。 import numpy as np import matplotlib.pyplot as plt original_image = plt.imread(image_name) if np.issubdtype(original_image.dtype, np.integer): original_image = original_image / np.iinfo(original_image.dtype).max gray_image = 0.2116 * original_image[:,:,0] + 0.7152 * original_image[:,:,1] + 0.0722 * original_image[:,:,2] plt.imshow(gray_image, cmap='gray') ガウシアンフィルターのカーネルを作成する関数と畳み込みを行う関数を定義します。ガウシアンフィルターの詳細は以下の記事を参照してください。 【画像処理】Numpyで平滑化フィルター - Qiita def _convolve2d(image, kernel): shape = (image.shape[0] - kernel.shape[0] + 1, image.shape[1] - kernel.shape[1] + 1) + kernel.shape strides = image.strides * 2 strided_image = np.lib.stride_tricks.as_strided(image, shape, strides) return np.einsum('kl,ijkl->ij', kernel, strided_image) def _convolve2d_multichannel(image, kernel): convolved_image = np.empty((image.shape[0] - kernel.shape[0] + 1, image.shape[1] - kernel.shape[1] + 1, image.shape[2])) for i in range(image.shape[2]): convolved_image[:,:,i] = _convolve2d(image[:,:,i], kernel) return convolved_image def _pad_singlechannel_image(image, kernel_shape, boundary): return np.pad(image, ((int(kernel_shape[0] / 2),), (int(kernel_shape[1] / 2),)), boundary) def _pad_multichannel_image(image, kernel_shape, boundary): return np.pad(image, ((int(kernel_shape[0] / 2),), (int(kernel_shape[1] / 2),), (0,)), boundary) def convolve2d(image, kernel, boundary='edge'): if image.ndim == 2: pad_image = _pad_singlechannel_image(image, kernel.shape, boundary) if boundary is not None else image return _convolve2d(pad_image, kernel) elif image.ndim == 3: pad_image = _pad_multichannel_image(image, kernel.shape, boundary) if boundary is not None else image return _convolve2d_multichannel(pad_image, kernel) def create_gaussian_kernel(size=(5, 5), sigma=1): center = ((size[0] - 1) / 2, (size[1] - 1) / 2) sigma2 = 2 * sigma * sigma kernel = np.fromfunction(lambda y, x: np.exp(-((x - center[1]) ** 2 + (y - center[0]) ** 2) / sigma2), size) kernel = kernel / np.sum(kernel) return kernel DoGフィルターを適用します。 def dog_filter(image, sigma1=2.0, sigma2=1.0, size=(7, 7), boundary='edge'): kernel1 = create_gaussian_kernel(size=size, sigma=sigma1) kernel2 = create_gaussian_kernel(size=size, sigma=sigma2) gauss_image1 = convolve2d(image, kernel1, boundary=boundary) gauss_image2 = convolve2d(image, kernel2, boundary=boundary) return gauss_image1 - gauss_image2 dog_image = dog_filter(gray_image) value_range = max(abs(dog_image.min()), abs(dog_image.max())) plt.imshow(dog_image, cmap='bwr', vmin=-value_range, vmax=value_range) plt.colorbar() 実装したコードはGoogle Colaboratoryに置いてあります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python開発者のためのセキュアコーディングのコツ10個

本記事は2021年9月27日に公開したPython security best practices cheat sheetを日本語化した内容です。 2019年、Snykは最初のPythonチートシートをリリースしました。それ以来、Pythonのセキュリティの多くの側面が変化しています。開発者向けセキュリティ企業として学んだこと、そしてPython特有のベストプラクティスに基づいて、Pythonのコードを安全に保つために、この最新のチートシートをまとめました。 【チートシート】2021年版Pythonセキュリティベストプラクティス 本記事では、下記に関するPythonのセキュリティに関するヒントを紹介します。 外部データを常にサニタイズする コードをスキャンする パッケージのダウンロードに注意 依存先パッケージのライセンスを確認する システム標準版のPythonを使用しない Pythonの仮想環境向け機能を利用する 本番ではDEBUG = Falseに設定する 文字列の書式設定に注意 (デ)シリアライズを慎重に行う Pythonの型アノテーションを使用する その前に、ひとつだけ注意点があります。SnykのPythonエコシステムに関するデータ、および学術研究によると、Pythonは他の広く使われている言語と比較して安全性が高い(または低い)わけではありません。心にとめておきましょう。このチートシートは、私たちPythonista(Python開発者)のために特別に用意したものです。他のエコシステムで安全である方法を学ぶために、弊社が作成している他のすべてのセキュリティチートシートをチェックすることをお勧めします。 1. 外部データを常にサニタイズする あらゆるアプリケーションの攻撃ベクトルの1つは外部データであり、インジェクション、XSS、サービス拒否(DOS)攻撃などに利用される可能性があります。Pythonのセキュリティを維持するための一般的なルールは、データがユーザー入力フォーム、ウェブサイトのスクレイピング、またはデータベースリクエストに由来するかにかかわらず、外部ソースからのデータを常にサニタイズ(要注意なデータを削除)することです。また、安全でない処理を防ぐために、データがアプリケーションに入るとすぐにサニタイズします。これにより、サニタイズされていない危険なデータが誤ってアプリケーションで扱われるリスクを減らすことができます。 サニタイズに手をつけると、例外処理をしようとするよりも、入力がどうあるべきかをチェックする方が常に理に適っていることに気づきます。また、サニタイズにはきちんと整備されているライブラリを使用することをお勧めします。以下はその2つです。 schemaは、「設定ファイル、フォーム、外部サービス、コマンドラインパースなどから取得され、JSON/YAML(または他の何か)からPythonデータ型に変換されたPythonデータ構造の検証を行うライブラリ」です。 bleachは、「マークアップと属性をエスケープまたは除去する、許可リストベースのHTMLサニタイズライブラリ」です。 主要なフレームワークには、Flaskのflask.escape()やDjangoのdjango.utils.html.escape()のような、独自のサニタイズ関数が付属しています。これらの関数の目的は、下記のような、潜在的に悪意のあるHTML入力から無害化することです。 >>> import bleach >>> bleach.clean('an XSS <script>navigate(...)</script> example') 'an XSS &lt;script&gt;navigate(...)&lt;/script&gt; example' この方法の限界は、ライブラリは万能ではないことです。ライブラリは、ある領域に特化しているのです。補足ですが、悪意のあるデータを含む可能性のあるXML他のデータ形式にも触れていますので、この記事の最後を必ず読んでください。 もう1つのよく使われる方法は、HTMLのレンダリングをJinjaのようなテンプレートエンジンに任せることです。これは多くの機能を提供しますが、その中でもMarkupSafeを使用したXSSを防ぐための自動エスケープ機能があります。 サニタイズのもう一つの側面は、データがコマンドとして使用されるのを防ぐことです。典型的な例としては、SQLインジェクションがあります。文字列や変数をつなぎ合わせてSQLクエリを生成するのではなく、名前付きパラメータを使って、何をコマンドとして扱い、何をデータとして扱うかをデータベースに伝えることが望ましいです。 # Instead of this … cursor.execute(f"SELECT admin FROM users WHERE username = '{username}'"); # ...do this... cursor.execute("SELECT admin FROM users WHERE username = %(username)s", {'username': username}); あるいは、sqlalchemyなどのORM(Object-Relational Mapping)を使えば、下記のようなクエリになります: query = session.query(User).filter(User.name.like('%{username}')) ここでは、より読みやすいコードと、キャッシュなどのORMの最適化、さらにセキュリティとパフォーマンスが向上しています! もっと詳しく知りたい方は、SQLインジェクションのチートシートをご覧ください。 2. コードをスキャンする 開発者は、Pythonのセキュリティを維持するために、さまざまな静的コード解析ツールを自由に使うことができます。ここでは、3つの異なるレベルのツールについて見てみましょう。 まず、リンターのレベルです。PEP8はPythonのスタイルガイドとして、もう何十年も役立っています。pep8、pylint、flake8など、このスタイルガイドと照らし合わせるためのさまざまなツールが利用可能です(IDEに組み込まれていることもあります)。 次に、banditのようなツールは、コードを抽象構文木(AST)に変換し、それに対してクエリーを実行して、典型的なセキュリティ問題を見つけます。これは、構文レベルで動作する一般的なリンターが行うものより一段高いレベルです。それでも、banditは中間表現と性能に限界があります。例えば、バンディットはデータフローに関連する問題(テイント分析として知られている)を検出できず、これらは破壊的な欠陥(例としてSQLインジェクションやXSSなどのインジェクション)につながります。 最後に、Snyk Codeのような静的アプリケーションセキュリティテスト(SAST)ツールは、複雑なファイル間の問題まで考慮に入れて、意味論的分析を実行します。このレベルの他のツールとは異なり、Snyk Codeは高速にスキャンし、IDE(または直接コマンドライン)に統合できるので、開発者フレンドリーなツールとなっています。Snyk Codeは、非常に正確な調査結果を説明し、Pythonのセキュリティ問題を修正する方法の例を含むヘルプを提供します。その上、簡単に始めることが可能で、無料でオープンソースに対してスキャンを行えます(さらに、オープンソースソフトウェア以外を対象にしたテストも制限の範囲内で利用できます)。 3. パッケージのダウンロードに注意 パッケージをインストールするのは簡単ですが、Pythonのセキュリティ脆弱性を導入する簡単な方法でもあります。通常、開発者はPython Package Index (PyPI) を利用する Pythonの標準パッケージインストーラ (pip) を使用します。このため、パッケージがどのようにPyPIに追加されるかを理解することが重要です。 PyPIには、セキュリティ上の懸念を報告するための手順があります。誰かが悪意のあるパッケージやPyPI内の問題を報告した場合、それに対処しますが、PyPIに追加されたパッケージはレビューを受けません - PyPIを保守するのがボランティアであることを考慮すると、レビューを期待するのは非現実的です。 したがって、PyPI内に悪意のあるパッケージが存在すると仮定し、それに従って行動することが賢明です。合理的な手順としては、インストールしたいパッケージについて少し調べ、パッケージ名を慎重に綴るようにします(一般的なパッケージの綴りを間違えたパッケージは、悪質なコードを実行する可能性があります)。パッケージをダウンロードする前に、必ずSnyk Advisorでチェックしてください。 Snyk Advisorでパッケージを検索すると、パッケージに関する多くの情報、コミュニティでのサポート、バグや修正の歴史、その他多くの情報を得ることができます。また、Snyk Advisorは結果ページの上部にインストールコマンドを表示します。URLハイジャッキング(typosquatting)を防ぐために、そのスペルをコピーして貼り付けるのがベストプラクティスです。Snyk Advisor は、パッケージを信頼すべきかどうかを教えてくれます。セキュリティ問題の履歴と、その修正に要した時間を見ることができます。 もう一つのベストプラクティスは、仮想環境を使ってプロジェクトを互いに分離することです。また、pip freezeまたは同等のコマンドを使用して、環境の変更をrequirements.txtファイルに記録します。 常に最新のリファレンスを維持するSnyk Open Sourceは、セキュリティ問題と可能な修正を記録した業界最先端の脆弱性データベースをベースにしています。Snyk Open Sourceでは、requirements.txtファイルを使用してスキャンを実行し、発見された直接および推移的な依存関係の脆弱性に関する実用的な情報を提供し、それらをすぐに修正することができます。 4. 依存先パッケージのライセンスを確認する オープンソースプロジェクトの利用を検討する場合、これらのプロジェクトがどのようにライセンスされているかを理解することが重要です。オープンソース・プロジェクトは無料で使用できますが、条件が適用される場合があります。これらの条件は通常、ソフトウェアの使用方法、ソフトウェアに加えた変更を一般に公開する必要があるかどうか、およびその他の類似の要件に関係します。あなたが利用するプロジェクトに必要なオープンソースライセンスに精通し、あなたが法的に妥協していないことを確認する必要があります。 プロジェクトが想定よりも厳格なライセンス(GPL、SSPL など) を採用している場合、ライセンスの条件を守るか、そのプロジェクトの使用を中止するかのどちらかを迫られ、自分自身を追い詰める結果になる可能性があります。さらに、ライセンスのないプロジェクトに変更を加える必要がある場合、著作権法に抵触する可能性があります。 あなたのプロジェクトが持続可能で、Pythonのセキュリティリスクや法的リスクに不必要にさらされないようにするために、あなたのプロジェクトが依存しているパッケージのライセンスと脆弱性の問題をスキャンして修正しましょう。 Snyk Open Sourceは、オープンソースライセンスのコンプライアンス管理で、サポートすることができます。柔軟なガバナンスを提供しながら、エンドツーエンドの可視性を得るための開発者向けのツールを提供しています。 5. システム標準版のPythonを使用しない ほとんどのPOSIXシステムには、Pythonのバージョンがプリロードされています。ほとんどのビルトインPythonディストリビューションの問題は、それらが最新ではないことです。 ですから、あなたのシステムで利用可能な最新バージョンのPythonを使用しましょう。Pythonの公式コンテナを使い、最新のバージョンに更新しておきましょう。Snykは常にあなたをサポートします。Snyk Containerを使用してコンテナをスキャンして必要な更新を行い、Snyk Open Sourceを使用して依存先のパッケージをチェックしてください。 6. Pythonの仮想環境向け機能を利用する Pythonは、アプリケーションの開発を仮想環境に分離する機能を備えています。仮想環境は、そこにインストールされたPythonインタープリタ、ライブラリ、スクリプトを分離します。つまり、すべてのプロジェクトでグローバルなPythonのバージョンとPythonのパッケージを使う代わりに、プロジェクト固有の仮想環境を用意して、独自のPythonとパッケージのバージョンを使うことができるのです! ほとんどの IDE、CLI、そしてAnaconda Navigatorのようなダッシュボードには、仮想環境間を切り替えるためのビルトイン関数があります。 ワンポイントアドバイス:Pythonバージョン3.5 では、venvの使用が推奨され、バージョン3.6ではpyvenvは非推奨となりました。 仮想環境は、安全なPythonアプリケーションの開発、パッケージ化、リリースをより簡単にします。仮想環境の利用は強く推奨されます。詳しくはPython venv docを参照してください。 7. 本番ではDEBUG = Falseに設定する 開発環境では、詳細なエラーメッセージを表示することは理にかなっています。しかし、実稼働環境では、攻撃者があなたの環境、ライブラリ、コードについてより詳しく知るための情報漏えいを防ぎたいものです。 デフォルトでは、ほとんどのフレームワークでデバッグのスイッチがオンになっています。例えば、Djangoはsettings.pyで有効になっています。攻撃者にアプリケーションの機密情報を漏らさないようにするために、実運用ではデバッグをFalseに切り替えることを忘れないようにしましょう。 ワンポイントアドバイス:本番環境にデプロイするとき、CD(継続的デプロイメント)システムに、デプロイ後はこの設定が無効になっていることを確認させると便利です。 8. 文字列の書式設定に注意 Pythonには「いいやり方はただひとつ」という考え方がありますが、実際には文字列をフォーマットする方法が4種類あります(Python 3.6より前のバージョンでは3種類)。 文字列の書式は徐々に柔軟で強力になってきていますが(f-stringは特に興味深い)、柔軟性が増すにつれて、悪用される可能性も高くなります。このため、Pythonのユーザーは、ユーザーが提供する入力で文字列をどのようにフォーマットするかを慎重に検討する必要があります。 Pythonにはstringという名前の組み込みモジュールがあります。このモジュールにはTemplateクラスが含まれており、テンプレート文字列を作成するために使用されます。 次の例を見てください。 from string import Template greeting_template = Template(“Hello World, my name is $name.”) greeting = greeting_template.substitute(name=”Hayley”) 上記のコードでは、変数greetingは次のように評価されます。"Hello World, my name is Hayley." と評価されます。 この文字列フォーマットはimport文が必要で、型に対する柔軟性に欠けるため、少し面倒です。また、f-stringのようにPythonのステートメントを評価することもできません。これらの制約により、テンプレート文字列はユーザー入力を扱う際に優れた選択肢となります。 文字列フォーマットに関して注意点をもうひとつ。上で述べたように、生のSQLの扱いには特に注意してください。 9. (デ)シリアライズを慎重に行う Pythonには、pickleモジュールを使ってPythonオブジェクトをシリアライズ/デシリアライズする "pickling "という仕組みが組み込まれています。これは安全でないことが知られており、非常に慎重に、信頼できるデータソースにのみ使用することが推奨されます。 シリアライズ/デシリアライズのための新しいデファクトスタンダードはYAMLです。PyYAMLパッケージは、カスタムデータ型をYAMLにシリアライズし、再び戻すためのメカニズムを提供します。しかし、PyYAMLはさまざまな攻撃ベクトルをはらんでいます。PyYAMLの使用を安全にするシンプルで効果的な方法は、ローダーとしてyaml.Loader()の代わりに yaml.SafeLoader()を使用することです。 Data = yaml.load(input_file, Loader=yaml.SafeLoader) これにより、カスタムクラスの読み込みはできませんが、ハッシュや配列などの標準的な型はサポートされます。 もう一つの典型的なユースケースはXMLです。標準的なライブラリがよく使われますが、典型的な攻撃、すなわちDOS攻撃や外部エンティティ展開(外部ソースが参照)に対して脆弱性があります。防御の第一線として、defusedxmlと呼ばれるパッケージがよいでしょう。これは、これらの典型的なXMLのセキュリティ問題に対する安全策を備えています。 10. Pythonの型アノテーションを使用する バージョン3.5から、型ヒントが導入されました。Pythonランタイムは型アノテーション(型注釈)を強制しませんが、型チェッカー、IDE、リンター、SASTなどのツールは、開発者がより明示的であることで利益を得ることができます。以下はそのアイデアを強調するための例です。 MODE = Literal['r', 'rb', 'w', 'wb'] def open_helper(file: str, mode: MODE) -> str: ... open_helper('/some/path', 'r') # Passes type check open_helper('/other/path', 'typo') # Error in type checker Literal[...]はバージョン3.8で導入され、ランタイムによって強制されることはありません(この例では好きな文字列を渡すことができます)が、型チェッカーはパラメータが許可された範囲に含まれないことを発見して警告してくれるようになりました。これは素晴らしい機能で、Pythonセキュリティ以外の面でも有用です。 注意:ランタイムによって強制されないので、型ヒントのセキュリティ上の使用は制限されています。 最後に Snykの公式ウェブサイトを公開しました。またSNSにて最新の脆弱性情報などを発信しているので、ぜひフォローをお願いします。 Snyk公式ウェブサイト Snyk Japan Twitter Snyk Japan Facebook またSnykは無料でお試しいただけます。ぜひお試しください! Snykを活用したブログは、ぜひQiitaアドカレ2021もご参照ください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む