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

Lambdaの計算を簡単に説明してみます

計算概要 実行時間とリクエスト 無料枠 無料枠超過分
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

clusterに暮らすキャラクターAIについて思うことと、つくり方 ( Living AI in cluster )

はじめに こんにちは。はじめまして。現在クラスター株式会社でインターンをしているhattoriです。クラスターでは主にUnityでの制作のお手伝いをしています。大学ではデータサイエンス(主に深層学習・機械学習)を専攻しており、現在はモーションの自動生成・最適化の開発に取り組んでいます。 今回の記事ではメタバースの分野で自分が一番興味のあるキャラクターAIについてご紹介させていただきます。クラスターでインターンをする中で気づきも活かし、個人的に開発しているキャラクターAIについての内容です。今回の記事がclusterのキャラクターAIについて興味のある方の参考になれば幸いと思っています。 一応こちらの記事はインターンの業務では全く無いです。ご注意ください。また、私個人による見解だけではなく、ネットで調べて得た情報や勉強会・イベントで教えて頂いた情報も含んでいます。ご了承ください。 目次 clusterにおけるキャラクターAI キャラクターAIのつくり方 プレイヤー操作をするAIモデル ClusterCreatorKitでつくるキャラクターAI つくったキャラクターについて clusterにおけるキャラクターAI キャラクターAIは、ゲームのNPCのように、キャラクターが環境認識して自律的に動くAIのことを指しています。今回紹介、つくっているキャラクターAIはclusterのプレイヤーとして振る舞い、簡単な応答もできるような対話型のキャラクターAIです。 clusterで対話型のキャラクターAIをつくるメリットは主に3つあると思っています。 - データの連続性を確認できる - 3D空間の人と人とのコミュニケーションにリアルタイムに関わることができる - キャラクターの自由な身体性 連続性のあるデータ データの連続性を確認できるというのは、対象のデータに紐づくデータを確認できるということです。clusterでは目視できる相手の場合、その人が「ワールドに入ってからワールドに出るまでの全てのデータ」を確認できます。例えば、〇〇さんが笑顔のスタンプを押したというデータを対象にした際、スタンプを押す前はどういった行動をしていたのか、その場にどういう人がいたのか、どういう会話をしていたのか等、全てのデータをとることができます。人が前後関係を考慮して対話するように、相手のことをちょっと察して話す対話型キャラクターを生み出しやすいと考えています。 3D空間の人と人とのコミュニケーションにリアルタイムに関わることができる マインクラフトといった3DゲームのAI開発は数多くされていますが、clusterのように日常会話がされているようば環境ではまだまだ少ないように思えます。日常会話に絞れば、エアフレンド といった有名なテキストベースの対話AIは現在あります。clusterのキャラクターAIでやりたいことは、3Dゲームとテキストベースの技術を組み合わせることです。身体性をもった日常会話に介入するキャラクターAIの存在は、新しい体験をもたらしてくれるかもしれないです。メタバースの取り組みが増えているため、まるでその仮想空間に暮らしているようなキャラクターAIの実現はそう遠くないと思います。 キャラクターの自由な身体性 3D空間でキャラクターの身体性が自由であるということは、結構当たり前のように感じるとは思うのですが、上記の「3D空間の人と人とのコミュニケーションにリアルタイムに関わること」を含めると特徴的な要素だと思います。日常的なコミュニケーションをイメージすると現実という物理的な制約によってできないことがありますが、clusterではそれが全く無いのです。 キャラクターAIがいることで生まれるコミュニケーション つくり方に入る前に、コミュニケーションに絞って補足させていただきます。対話キャラクターAIがいることで、新しいコミュニケーションが生まれると思っています。 AIがいない場合 人と人とのコミュニケーション AIがいる場合 人と人とのコミュニケーション AIがいることで人と人とのコミュニケーションの場が和む 人とAIと人のコミュニケーション 直接言えないことをAIが代わりに伝える (気が合うはずだけど、お互い知らない人同士のコミュニケーション) AIがいることで人と人とのコミュニケーションの場が和む これは丸の内のKITTEの中にあるGOOD DESIGN STORE TOKYO by NOHARAでLOVOTというロボットが接客しているのを見て考えたことです。何回か行ったことがあったお店だったのですが、その日だけとても和やかに感じました。犬や猫がいると場が和むことに近い感覚でした。猫や犬といった生命が人を癒したり、場を和ませたりするのはよくあることだとは思います。ですが、生命に近いとは言えど人工的に作られたものがそういった影響をもたらすことは新しい発見でした。LOVOTについて調べてみると「自然と顔がニコニコ」という意見が多く集まっています。 黒谷友香、ロボットと生活で母性 充実の今に「自然と顔がニコニコ」 人が多くなるにつれて、コミュニケーションのトラブルがあるとは思うのですが、AIがそういった場を起こしにくくする可能性があると思いました。 人とAIと人のコミュニケーション これもLOVOTがお店にいる時思ったことです。店員さんとお客さんがLOVOTとコミュニケーションをとっている様子をみるという今までにないお店での体験をしました。 ポケモンも例に出させていただきます。こちらはアニメの例になるのですが、ポケモンがいることで、「人とポケモンと人のコミュニケーション」が生まれているのだと思いました。 AIがいなかったら相手のことをよく知らずコミュニケーションが終わるっということも少なくないかもしれないです。 直接言えないことをAIが代わりに伝える これも人と人とのトラブルに関する要素です。直接言えないけど言うべきことをAIなら伝えることができるかもしれないという内容です。 (気が合うはずだけど、お互い知らない人同士のコミュニケーション) 「人とAIと人のコミュニケーション」では、コミュニケーションをする中でで、AIがいることで更に相手のことがわかるという内容でしたが、こちらはそもそもコミュニケーションにつながっていない人たちを対象にしています。よく人と人とをうまく繋げる方がいらっしゃると思うのですが、その方のように、あらゆるコミュニケーションデータから、「気が合うはずだけどけど知らない人と知らない人」をつなげることも可能性としてあると考えています。 clusterのキャラクターAIのつくり方 clusterでのキャラクターAIの作り方を2つ紹介します。 - プレイヤー操作をするAIモデル - ClusterCreatorKitでつくるキャラクターAI cluster内で対話可能で、暮らしているように振る舞うようなAIは現状1つ目の方法でないと厳しいです。 プレイヤー操作をするAIモデル AIの最新技術をclusterに組み込むための手法の1つです。PyAutoGuiというRPA(Robotic Process Automation)ツールを用います。Pythonで実装することができるので、Python側で、意思決定、物体検知、言語処理、音声処理の処理を用意することで、その処理結果をclusterの操作に反映することができます。デバイスを操作するロボットを作るという手段もあるとは思います。 意思決定 現状は、シンプルなビヘイビア・ツリーで構成されています。暮らしているようなAIにするために最適なモデルを現在追求しています。 今年のCEDECでも発表があったのですが、感情を持ったAIをつくる取り組みがされており、こういった「感情」はもっともこのキャラクターAIに組み込みたい要素だと考えています。 [CEDEC 2021]知性と感情を持ったNPCとのコミュニケーションを実現するスクウェア・エニックスの取り組み 物体検知 YOLOv5といった物体検知モデルを使うことで、プレイヤーの行動を認識します。そしてプレイヤー自体もこちら認識します。 ここでの気づきなのですが、プレイヤーの上にあるidが表示されないと、その人のことを覚えることができないということです。今までちょっと邪魔かも...と思ったのですが、アバターの変化ができるclusterにおいてはその人の識別に必要不可欠な要素となっています。初めての人にとっては覚えるための強いシンボルになってるかもしれないです。 言語処理 GPT-3といった大型で高性能の言語処理モデルを使いたいところですが、まだ実装できていないです。 ワールド内でのコメントやメッセージのコメントを取得し、言語分析をして相手のことを把握したり、言語を生成してPyAutoGuiによってclusterで送信することを考えています。 音声処理 こちらもまだ未実装です。音声の検知によって相手の感情を認識している例もあるので、実装は進めていきたいです。 ClusterCreatorKitでつくるキャラクターAI ClusterCreatorKitでのAI現状難しいです。その中でもキャラクターAIをつくるのに活かせる手法をご紹介します。 コリジョンセンサーを回してプレイヤーを検知 こちら他のクリエイターの方が発信されているものです。後ほどリンクを載せさせていただきます。 アニメーションのステートマシン こちらも他のクリエイターの方が発信されているものです。後ほどリンクを載せさせていただきます。 意思決定オブジェクトの生成の繰り返し 意思決定を決めるオブジェクトを生成し、破壊されるときに条件分岐を利用して生成を繰り返すことで、意思決定に近い表現を実現します。 リアルタイムのアップロードでキャラクターを変化させる(プレイヤー操作をするAIモデルの検知を活かす) 1つ目の手法で検知した情報をもとに、ワールドのアップロードの内容を変更するという手法です。現状かなり非現実的な手法ではあります。 つくったキャラクターについて 2つのコンセプトを持って、2つのキャラクターAIをつくっています。ワールドで見かけたら是非声をかけてみてください。 人との関わりによってキャラクターが成長します。 和み ツムグ 「場を和ませるような、人と人とをつなぐ人型AI」 この記事で主張が多い「キャラクターAIがいることで生まれるコミュニケーション」を目指してつくっているキャラクターAIです。理想はclusterのコミュニケーショントラブルを減らせれることです。 データを取得や行動処理をすることでエラーを起こすことがあります。その際は、膝をついて目をぐるぐるさせてログアウトしちゃいます。突然の行動でびっくりするかもしれないですが、そっとしてもらえると助かります。 c.001 「小さくて丸っこい、ノンバーバル(非言語)なロボット型AI」 clusterでは「ピュッピュー」(スタンプ)でコミュニケーションが取れることをアイデアにしたノンバーバルなキャラクターAIです。AIを考える中で1つ課題感を感じていたのが、綺麗な人型の場合近寄り難く感じてしまうということです。人より小さくて、ちょっとダサい(良い意味で です。)からこそ近寄ることができるのだと思います。スターウォーズのR2D2のようなものと考えるとイメージしやすいと思います。 おわりに ここまで記事を読んでくださりありがとうございます。clusterでは様々なコミュニケーションがされているとおもうのです、記事でも書いたように、キャラクターAIによって新しいコミュニケーションができる可能性についてお話しさせていただきました。まだ上手くいくかわからない状況ですが、ちゃんと役割を持つように共生できることを目指していきたいです。 今までAIエンジニアとして他の会社でインターン・共同研究の経験があるのですが、そういったAIの観点のからも、clusterのデータの特性に興味を持っていました。ソーシャルなデータだからこそ、SAOのアリスようなAIがうまれるのではないのかと淡い期待を持っていました。こちらのAIモデルは最終的に色んな人でもつくり育てることができるようにすることを考えています。clusterで様々なワールドをつくっているように、様々なキャラクターを生み出して、ポケモンの世界のように人とは違った生命がいるからこそ楽しい世界がうまれるかもしれないです。 (ポケモンGO公式サイトより、https://www.pokemongo.jp/news/540/)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【2日目】Python初心者が開発環境を作る

やること 開発環境の選定 自分の環境にインストールする 私の環境はMacなので、ご参考にされるかたがWindowsの場合は若干違うかもしれません。 Pythonパッケージ 以下は、Python 開発環境 でググった結果からわかったことです。 まずはPythonのインストールをする必要があるようでした。 Pythonのパッケージはアナコンダがおすすめということなので、これをインストールします。 ページ下部にインストーラへ誘導するリンクがありました。※2021年12月時点 手順に沿ってインストール アナコンダ起動 アナコンダのインストールが終わったら、アナコンダナビゲーターというソフトを起動し、JupyterLabで開発します。 てっきりアナコンダが開発ソフトかと思ってました! アナコンダナビゲーターを起動後、JupyterLabと書いてあるところのLanchをクリックするとブラウザが立ち上がます。 画像の画面が開きました。これが開発画面のトップページ的なものです。 試しにプログラミング 新規のブックを作成して、「hello world」してみます。 下記コマンドを書いて、Shift+Enterでプログラムを実行すると・・・ print('hello world') 世界にあいさつができました! 今日はここまで。ご一読ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【python】pandas総まとめ(apply / concat)

はじめに バージョン Python: 3.6.8 pandas: 1.1.5  Pandasは、Pythonでデータ分析を行うためのライブラリです。複数のデータに処理を行いたい場合、 for文で書くことが多いと思いますが、pandasで一度DataFrame(表みたいなもの)に変換してから処理を 行うようにすると、シンプルにコードが書けるようになります。 サンプルデータ  まずは以下をサンプルデータとします。列col_aとcol_bがあり、行ごとの計算結果を出力します。 import pandas as pd df = pd.DataFrame( data=[[1, 2], [3, 4], [5, 6]], columns=["col_a", "col_b"], ) DataFrameとしては以下のようになっています。 col_a col_b 0 1 2 1 3 4 2 5 6 1列のデータをもとに新たな1列を追加(lambdaを使用)  列col_aの値を2倍した結果を、新たな列a*2に出力します。df["col_a"] の部分で列col_aを抽出し、 lambda関数内の変数x(文字は任意)に適用させます。計算結果は新しい列a*2に出力されます。 df["a*2"] = df["col_a"].apply(lambda x: x * 2) DataFrameとしては以下のようになっています。 col_a col_b a*2 0 1 2 2 1 3 4 6 2 5 6 10 1列のデータをもとに新たな1列を追加(関数を使用)  ここでも列col_aの値を2倍した結果を、新たな列a*2に出力します。(出力結果は1つ前と同じなので省略) 1つ前ではlambdaを使用しましたが、ここでは関数value_multipicationを使います。 この関数は、引数として渡された2つの値の積を出力します。 パターン1 1つ前でlambdaを使った場合と似ていると思います。 パターン2 lambdaを使わない場合の書き方です。このように書くと、関数value_multipicationの 第1引数に列col_aの値が自動的に代入されます。第2引数は関数で使っている変数名と値を 記載(value_2=2の部分)する必要があるので注意が必要です def value_multipication(value_1, value_2): return value_1 * value_2 df["a*2"] = df["col_a"].apply(lambda x: value_multipication(x, 2)) # パターン1 df["a*2"] = df["col_a"].apply(value_multipication, value_2=2) # パターン2 2列のデータをもとに新たな1列を追加  ここでは列col_aとcol_bの積を、新たな列a*bに出力します。 2列以上のデータを使用する場合は、df.applyのように列名を記載しません。その代わりに、 lambda関数内で列名を指定します。また、axis=1と書くことで、applyを行方向に適用する ことを宣言する必要があります。 df["a*b"] = df.apply(lambda x: x["col_a"] * x["col_b"], axis=1) DataFrameとしては以下のようになっています。 col_a col_b a*b 0 1 2 2 1 3 4 12 2 5 6 30 2列のデータをもとに新たな2列を追加  ここでは列col_aとcol_bの2倍の値を、それぞれ新たな列a*2とb*2に出力します。 複数列を出力(今回は2列)する場合は、apply内の計算結果をリストにして、要素数は 出力する列数と同じにします。さらに、result_type="expand"を追加することで、 リストが展開されて新たな列が作成されます。 なお、axis=1がないとresult_type="expand"が使えないので注意が必要です。 df[["a*2", "b*2"]] = df.apply( lambda x: [x["col_a"] * 2, x["col_b"] * 2], axis=1, result_type="expand" ) DataFrameとしては以下のようになっています。 col_a col_b a*2 b*2 0 1 2 2 4 1 3 4 6 8 2 5 6 10 12 最終行に列ごとの合計値を追加  ここでは、最終行に列ごとの合計値を追加します。 concatを使い、もともとのDataFrame(df)と列ごとの合計値のDataFrameを結合させます。 df_new = pd.concat([df,pd.DataFrame(df.sum(), columns=["result"]).T]) DataFrameとしては以下のようになっています。 col_a col_b 0 1 2 1 3 4 2 5 6 result 9 12 列ごとの合計値のDataFrameを作成する部分を詳しく見ていきます。 以下のように列ごとの合計値を出力すると、結合させたいDataFrame(df)に対して行と列が 逆になっていることがわかります。 pd.DataFrame(df.sum(), columns=["result"]) result col_a 9 col_b 12 そこで、DataFrameに対してT(transpose)を追加することによって、行と列を入れ替えることができます。 この状態であれば、もともとのDataFrame(df)と結合することが可能です。 pd.DataFrame(df.sum(), columns=["result"]).T col_a col_b result 9 12 参考 pandas.DataFrame.apply — pandas 1.3.4 documentation pandas.DataFrame.transpose — pandas 1.3.4 documentation 関連記事 過去に作成したpandas関連の記事です。 【python】pandasの活用~1回目:DataFrameの作成、applyの使い方~ 【python】pandasの活用~2回目:axisの使い方~ 【python】pandasの活用~3回目:Excelのデータ取り込み~ 【Ansible×pandas】Excelからデータの入力/出力がしたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AKAZEを用いた特徴量マッチングによるCDジャケ買いアプリ

このブログはAidemyさんで受講した講座「AIアプリ開発」の課題である オリジナルアプリ製作についての内容になります。あとは受講の経緯など…。 唐突な自分語りお許しください お世話になります、僕です。 製造業で技術職をしております。主に機械の設計、図面を描く、客先へ納品した 設備の試運転、設備の定期メンテナンス及びトラブル対応、といったことを 生業としております。 ちなみに技術職になるまえは同社内で営業職をしていました。 営業4年→技術5年という形です。 なんでもできる丈夫な子に育ってほしいという思いが伝わってきます。 プログラミングに全くと言えるほど縁がない僕が受講した理由は このままこの仕事してても、近い将来食いっぱぐれんじゃね? という恐怖があったからになります。 はじめに恐怖がありました しかしこのブログを書くにあたってもmarkdownとか急に言われてもなぁという程度の 知識レベルの私。 正直最初はAidemyさんのカリキュラムの説明文読んでも「ふむふむ。日本語でおk」 状態でしたので、そもそも基礎知識が足りなすぎると感じ、amazonでかえるの表紙の本を 買って読んだりしてました。 学習の進みが遅く、チューターさんからはよく心配をされていましたが、 ある時に「いいからやってみそ、最初から全部理解する必要はないっす」 という励ましをもらいなんとかやってきた形です。 なので今は2週目をやったりしています。 2週目はひらがなは読めるようになったかなというくらいの感じがします。 いっぱい質問しよう、とは思うんですけど… Aidemyさんには6か月コースでお世話になったのですが、いきなり全部わからんで良いと 言われつつもやはり「ンギギ…!」とか「ンゴゴ…!」という感じで頭を抱えていました。 新しい知識、しかも経験のない分野の学習は楽しい反面、しんどいです。 また急に生活が多忙になるなんてこともあるので、最終課題まで一通り完了してから 復習しましょう、ということはお伝えできるかと思います。 あとカウンセリングはどんどん受けた方がいいですね。私は相当な回数が余って しまいました。 とはいえ、質問するには何が分からないかを理解できないとならないんですよね。 分からないことが分からない。ここを抜けてようやく初心者脱出でしょうか。 はい、初心者でございます 僕はCDのジャケ買いが好きでした。唐突ですね。現職と関りも全然ないですね。 それはそれとして。今や音楽もデジタルがメインになって、フィジカルで買わなくなって 久しいですが、今でもジャケ買いにはロマンがあると思っています。 CDなので本体は収録されている楽曲になるわけですが、その前に手に取って貰えるように デザインされていなくてはなりませんし、またCDは全て試聴できるわけでもないので、 ほぼ見た目だけで買ってもらうところまでいく必要があるわけです。 ロマンというよりギャンブルか…。 ビジュアル的に気に入る→中身の音楽も気に入るだろうというのが (もちろん外れもありますが)ジャケ買いの楽しいところです。 というわけでそんな感じのアプリを制作してみました。 ジャケ買いしよう(提案) 送信された画像の特徴量と、登録された40枚のCDジャケット(私有のものを写真撮影) の特徴量をマッチングし、送信された画像と一番似ているジャケットのCDのタイトル・ アーティスト名を表示するというものです。 ジャケ買い=気に入ったデザインのCDを買うことと考えると、つまり気に入ってる画像と 似たCDを見つけることと同義なんじゃないかと…。 特徴量マッチングにはAKAZEを使用しています。参考としたのはこちらのサイトです↓ OpenCVのAKAZEで顔写真の類似度判定をやってみた 判定の流れとしては ・入力された画像を読み込み、特徴量を抽出 ・AKAZEを適用、特徴点を検出 ・距離を記録するリストを作成 ・比較する画像を読み込み、特徴量を抽出 ・matcherで入力された画像と比較する画像の距離を算出 ・距離の平均を算出 という流れになります。アプリ化するに当たってこの部分は関数化されています。 またCDジャケットという性質上、1タイトルにつき1枚なので、比較する画像は常に1体1となります。 ※これを書いていて、裏表紙も背表紙もあるやんけ、と気づきましたが…。 たった40枚分のデータを集めるにも相応に時間がかかりましたが、本当はこの世のあらゆる CDジャケットと比較したいんですよね。イメージとしては通販サイトなどと連動して その膨大なCDジャケットの画像と比較する、これが最終形態ですかね。 これからどうしよう、どうしたい? 最終目標は転職ですが、飯を食うレベルのスキルを着けるのは大変ですよね。 精進は引き続きするとして、なかなか年齢とスキルの壁は高いです。 現職のスキルと合わせ技でなんとかしたいところです。 終わりに まだまだプログラミングの世界をかすった程度と思いますが、実際に初めてみたら ネットにいっぱいヒントが載ってるし、質問できる場があるということが知れたので、 今後も続けていけるなという実感はあります。 (Aidemy Gritもありますしね!) 以上、ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NECプログラミングコンテスト2021(ABC229) A~E問題 ものすごく丁寧でわかりやすい解説 python 灰色~茶色コーダー向け #AtCoder

NECプログラミングコンテスト2021(AtCoder Beginner Contest 229) A~E問題の解説記事です。 灰色~茶色コーダーの方向けに解説しています。 その他のABC解説、動画などは以下です。 日本電気株式会社(NEC)様について コンテストのトップページから 「なぜNECがプログラミングコンテストを実施するのか」 「リサーチエンジニアの業務内容」 を確認できます。 また『2021年10月~12月下旬に、学生の皆さんを対象としたイベント「中央研究所 見学交流会(修士/博士対象)」をオンラインで開催します!』とのことなので、興味がある方はご確認ください。 A - First Grid 問題文が長くてややこしくてうんざりしますが頑張って読みましょう。 条件をしっかり確認すると「No」になるパターンは以下の2つだけであることがわかります。 黒マスが左上と右下 S1:#. S2:.# 黒マスが右上と左下 S1:.# S2:#. ifを使ってS1,S2がこのどちらかになっているか確認し、条件分岐します。 どちらでもなければ「Yes」となります。 「Yes」「No」は文字列なので出力の際、"Yes","No"とダブルクオーテーションをつけてください。 入力の受け取り、出力がわからない方は以下の記事を参考にしてください。 【提出】 # 入力の受け取り S1=input() S2=input() # 黒マスが左上と右下 if S1=="#." and S2==".#": # 「No」を出力 print("No") # 黒マスが右上と左下 elif S1==".#" and S2=="#.": # 「No」を出力 print("No") # それ以外 else: # 「Yes」を出力 print("Yes") B - Hard Calculation 各桁の足し算が一つでも10以上であれば「Hard」、全て9以下であれば「Easy」となります。 各桁計算して確かめましょう。 実装の手順は以下です。 (1)A,Bは文字列で受け取る 数値で受け取ると各桁の計算が面倒です。 (2)A,Bはひっくり返す 例えばA=1234、B=54321の場合(A,Bの桁数が違う場合) A[0]=1=千の位 B[0]=5=万の位 となって桁がずれます。 そこでA=4321、B=12345とひっくり返します。そうすると A[0]=4=一の位 B[0]=1=一の位 となるので後の実装が楽になります。 文字列をひっくり返すときは 文字列[::-1] と書きます。よく使う書き方ですが「python 文字列 反転」などでググると出てくるのでいちいち覚える必要はありません。 (3)桁数の小さい方がいくらかを確認する 例えばA=1234、B=54321の場合、Aは4桁、Bは5桁です。この場合は4桁だけ確認すればOKです。 桁数=文字数なので、Aの桁数はlen(A)、Bの桁数はlen(B)です。 桁の小さい方を確認するときは min(len(A),len(B)) と書けば良いです。 (4)各桁の足し算を確認する A,Bのi桁目はそれぞれA[i],B[i]となります。 このままでは文字列扱いとなってしまい、足し算ができません。そこでintを使って整数へ変換してから足し算します。 int(文字列) と書くことで文字列→整数へ変換できます。 あとは(3)で確認した桁数回足し算して確認すればOKです。 (5)足し算が一つでも10以上なら「Hard」を出力して終了 途中でプログラムを終了する場合は exit() と書きます。 全て9以下なら「Easy」を出力します。 【提出】 # 入力を文字列で受け取り A,B=map(str,input().split()) # ひっくり返す A=A[::-1] B=B[::-1] # 桁数の小さい方 keta=min(len(A),len(B)) # i=0~(桁数の小さい方) for i in range(keta): # i桁目を整数へ変換 A_int=int(A[i]) B_int=int(B[i]) # 各桁の足し算が10より大きければ if 10<=A_int+B_int: # 繰り上がりがある # 「Hard」を出力 print("Hard") # 終了 exit() # 繰り上がりなし # 「Easy」を出力 print("Easy") C - Cheese 貪欲法を使います。 貪欲法はきちんと説明すると難しいのですが、大雑把に「前後の状況は考えずに今一番良いものを取り続ける方法」と覚えてください。 詳しく知りたい人はWikipediaをご覧ください。 この問題では単純に美味しさの大きいチーズから可能な限り載せ続ければ良いです。 まずチーズの情報を美味しさが大きい順に並び替えます。 リストを大きい順に並び替えるには以下のように書きます。 (リスト).sort(reverse=True) そしてチーズの情報を確認し、残り載せられる重さをWとして ・チーズの重さ≤Wなら→全部載せる ・W<チーズの重さなら→W[g]分載せる とします。 【提出】 # 入力の受け取り N,W=map(int,input().split()) # チーズの情報 cheese=[] # N回 for i in range(N): # 入力の受け取り A,B=map(int,input().split()) # 情報の格納 cheese.append([A,B]) # 美味しさの大きい順にソート cheese.sort(reverse=True) # 答え ans=0 # i=0~(N-1)まで for i in range(N): # i種類目のチーズの美味しさ delicious=cheese[i][0] # i種類目のチーズの重さ weight=cheese[i][1] # 重さ≤Wなら if weight<=W: # 全部載せる ans+=delicious*weight # 載せられる残りの重さ W-=weight # そうでないなら(W<重さなら) else: # W[g]分載せる ans+=delicious*W # forを抜ける break # 答えの出力 print(ans) D - Longest X 尺取法を使います。 例として以下の入力を考えます。 S:XX...X.X.X. K:2 まず「区間[左,右]について全て「X」とすることはできるか?」から考えましょう。 つまり区間[左,右]について、その区間の「.」を全て「X」に変えられるか?ということを考えます。 例えば区間[5,9]だと「5≤i≤9についてS[i]を全てXにできるか」=「Sの5~9番目を全てXにできるか」ということです。 ※ただしSは0インデックス、すなわち先頭をS[0]=0番目、次をS[1]=1番目、...と数えます。 [5,9]について「.」を全て「X」に変えられるか?を考えるならS[5]~S[9](「X.X.X」)に含まれる「.」の数を数えればよいです。 「.」の数がK以下ならば変えられます。(「.」の数は2個、K=2なので変えられる) ところがS[5]はなにか,S[6]はなにか,...と一個一個「X」か「.」か確認していては当然TLEします。 そこで累積和を使います。 以下のように「0~i番目までに含まれる「.」の数をカウントするリスト」を作ります。名前はcountです。 i番目までの「累積」の「.」の数を数えているので「累積和」です。 累積和を使うと区間に含まれる「.」の数を簡単に計算できます。 具体的には以下のように計算します。 ・左=0の場合 [左,右]に含まれる「.」の数=count[右] ・それ以外の場合(1≤左) [左,右]に含まれる「.」の数=count[右]-count[左-1] [5,9]であれば [5,9]に含まれる「.」の数=count[9]-count[5-1]=count[9]-count[4]=5-3=2となります。 S[5]~S[9]=「X.X.X」ですから確かに「.」の数は2個です。 countは以下の計算で作ります。 ・count[0] S[0]=「X」の場合:count[0]=0 S[0]=「.」の場合:count[0]=1 ・count[i](1≤i) S[i]=「X」の場合:count[i]=count[i-1] S[i]=「.」の場合:count[i]=count[i-1]+1 S[i]が「X」ならひとつ左の要素をそのまま持ってくる、「.」ならばひとつ左の要素+1とするというイメージです。 これで区間[左,右]に対して高速で「.」の数を求めることはできました。 しかし全ての[左,右]の区間を確認するとSの長さをNとしてO(N^2)かかるのでやはりTLEします。 そこで尺取法を使います。 よくよく考えてみると[左,右]について順に確認する時、右は戻る必要がありません。 例えば 「[1,5]の区間は「.」を全て「X」に変えられる」 となっていれば 「[1,3]の区間は「.」を全て「X」に変えられる」 ということが自動的にわかります。つまり右については5より小さい数を確認する必要はないということです。 よって右は5より大きい数、すなわち[1,6],[1,7],...を確認すればよいです。 この考えをベースに以下の手順で計算、確認します。 (1)「答え」を0とする (2)右=0とする (3)左=0~N-1まで順に増やす (4)[左,右]が全て「X」に変えられるか計算する ・変えられる  右をプラス1 ・変えられない  [左,右-1]の長さ(全て「X」に変えられた区間の長さ)を計算→「答え」より長い区間なら「答え」を更新  次の左へ((2)へ戻る) 左と右の動き方が尺取り虫のようなので、この方法を「尺取法」と呼びます。 実装にはforとwhileを使います。 慣れるまで少し難しく感じると思いますが、尺取法はよく使う割にみんな苦手なのでできるとレートが上がりやすいです。頑張りましょう。 【提出】 # 入力の受け取り S=input() K=int(input()) # Sの長さ N=len(S) # 「.」の累積個数 count=[0]*N # S[0]が「.」ならば if S[0]==".": # count[0]に1を入れる count[0]+=1 # i=1~(N-1)まで for i in range(1,N): # S[i]=「X」ならば if S[i]=="X": # 一つ左と同じ count[i]=count[i-1] # そうでないなら(S[i]=「.」) else: # 一つ左+1 count[i]=count[i-1]+1 # 答え ans=0 # 右 right=0 # left=0~(N-1)まで for left in range(N): # right<Nの間 while right<N: # もし左が0なら if left==0: # [左,右]にある「.」の数=count[right] sum_section=count[right] # そうでないなら(1≤左) else: # [左,右]にある「.」の数=count[right]-count[left-1] sum_section=count[right]-count[left-1] # [左,右]にある「.」の数≤Kならば # ⇔[左,右]にある「.」を全て「X」に変えられるなら if sum_section<=K: # 右を移動 right+=1 # そうでないなら([左,右]にある「.」を全て「X」に変えられないなら) else: # whileを抜ける break # [左,右-1]の長さを計算し、今までの答えより大きければ更新 ans=max(ans,right-left) # 答えの出力 print(ans) E - Graph Destruction UnionFindを使います。 UnionFindはグループ分けを高速でできるデータ構造です。 具体的には以下のことができます。 ・a,bを同じグループとする⇔a,bを連結する:【O(logN)くらい】(厳密には違いますがO(logN)くらいと思っておけばOKです) ・a,bが同じグループか判定する⇔a,bが連結か確認する:【O(1)くらい】(厳密には違いますがO(1)くらいと思っておけばOKです) とにかくめちゃくちゃ速く上記2つができるデータ構造だと覚えましょう。 きちんとした説明はAtCoder公式が解説スライドを作っているのでそちらを御覧ください。 UnionFindの実装はかなり難しいです。ですが問題を解くのにそこまで理解する必要はなく、以下のUnionFindクラスをコピペして使えればOKです。 # UnionFind class UnionFind: def __init__(self,n): self.n=n self.parent_size=[-1]*n def leader(self,a): if self.parent_size[a]<0: return a self.parent_size[a]=self.leader(self.parent_size[a]) return self.parent_size[a] def merge(self,a,b): x,y=self.leader(a),self.leader(b) if x == y: return if abs(self.parent_size[x])<abs(self.parent_size[y]):x,y=y,x self.parent_size[x] += self.parent_size[y] self.parent_size[y]=x return def same(self,a,b): return self.leader(a) == self.leader(b) def size(self,a): return abs(self.parent_size[self.leader(a)]) def groups(self): result=[[] for _ in range(self.n)] for i in range(self.n): result[self.leader(i)].append(i) return [r for r in result if r != []] 上記をコードの最初にコピペしてから以下のように使います。 ・初期化:変数名=UnionFind(要素の数) ・根の確認:変数名.leader(要素番号) ・グループ化:変数名.merge(要素番号1,要素番号2) ・同一グループかの確認:変数名.same(要素番号1,要素番号2)  (同一ならTrue,違うグループならFalseを返す) ・所属するグループのサイズ確認:変数名.size(要素番号) ・グループ全体の確認:変数名.groups() 【初期化時の注意事項】 本問のように頂点の番号が1から始まる場合、 初期化:変数名=UnionFind(N+1) としなければならないことに注意してください。 このクラスは頂点番号が0インデックスでの使用を想定しているためです。 【使用例】 # 初期化:変数名=UnionFind(要素の数) UF=UnionFind(10) # グループ化:変数名.merge(要素番号1,要素番号2) UF.merge(0,2) UF.merge(1,3) UF.merge(3,0) # 根の確認:変数名.leader(要素番号) leader_x=UF.leader(1) # 同一グループかの確認:変数名.same(要素番号1,要素番号2) if UF.same(1,5)==True: print("同一グループ") else: print("別グループ") # 所属するグループのサイズ確認:変数名.size(要素番号) size_x=UF.size(1) # グループ全体の確認:変数名.groups() print(UF.groups()) 「連結」という言葉が出てきたときはだいたいUnionFindで解けます。 しかしUnionFindに頂点や辺を削除する機能はありません。できるのは ・頂点をつなげる ・頂点同士がつながっているか確認する の2つです。 そこで逆順で考えます。 つまり頂点Nまで消された何もない状態から、頂点N,頂点N-1,...を順に戻していくと考えます。 まず入力を受け取る時、通常無向グラフではA→B,B→A両方へ進めるとします。 が、頂点を番号の大きい順に戻していくので、A→B(小→大)へは進めてもB→A(大→小)へは進めないです。(頂点Bを戻した時点では頂点Aはまだ戻っていません) よってA→Bの有向グラフとして入力を受け取ります。 ※条件にAi<Biがあることに注意してください 頂点を1個戻すとその時点で連結成分が1個増えます。 戻した頂点から進める頂点についてUnionFindで連結かどうか?を確認します。 ・連結でない場合  頂点同士をつなげる⇔同じグループとする。  同じグループになったから連結成分が1個減る。 ・連結の場合  すでにつながっているなので何もしなくて良い。 このように各頂点の情報を確認しながら連結成分の数を増やしたり減らしたりして記録していきます。  UnionFindのより詳細な説明、クラスの内容の解説については拙著『AtCoder 凡人が『緑』になるための精選50問詳細解説』に記載しています。 内容までしっかり理解しておきたいという人は購入をご検討ください。 詳細は本ページ下部【広告】を御覧ください。 【提出】 # UnionFind class UnionFind: def __init__(self,n): self.n=n self.parent_size=[-1]*n def leader(self,a): if self.parent_size[a]<0: return a self.parent_size[a]=self.leader(self.parent_size[a]) return self.parent_size[a] def merge(self,a,b): x,y=self.leader(a),self.leader(b) if x == y: return if abs(self.parent_size[x])<abs(self.parent_size[y]):x,y=y,x self.parent_size[x] += self.parent_size[y] self.parent_size[y]=x return def same(self,a,b): return self.leader(a) == self.leader(b) def size(self,a): return abs(self.parent_size[self.leader(a)]) def groups(self): result=[[] for _ in range(self.n)] for i in range(self.n): result[self.leader(i)].append(i) return [r for r in result if r!=[]] # 入力の受け取り N,M=map(int,input().split()) # 辺の情報 connect=[[] for i in range(N+1)] # M回 for i in range(M): # 入力の受け取り A,B=map(int,input().split()) # A→Bへ辺が張れる connect[A].append(B) # UnionFindを定義 UF=UnionFind(N+1) # 答えの格納用 ans=[0]*(N+1) # 現在の連結成分の数 count=0 # i=N~1まで for i in range(N,0,-1): # 頂点iを追加 # 連結成分が一つ増える count+=1 # 頂点x=頂点iと結ばれる頂点 for x in connect[i]: # 頂点iと頂点xが連結でないなら if UF.same(i,x)==False: # 頂点iと頂点xをつなげる UF.merge(i,x) # 連結成分が一つ減る count-=1 # 答えを格納 ans[i-1]=count # i=1~Nまで for i in range(1,N+1): # 答えの出力 print(ans[i]) 【広告】 「AtCoder 凡人が『緑』になるための精選50問詳細解説」 AtCoderで緑になるための典型50問をひくほど丁寧に解説した本(kindle)、pdf(booth)を販売しています。 値段:100円(Kindle Unlimited対象) 【kindle】 【booth(pdf)】 1~24問目まではサンプルとして無料公開しています
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SAM(のコード)を理解するための記事

はじめに 修士学生はいろいろな論文を読んで実装しているかと思います。 そんな中で,自作で様々なものを作成できる必要があります。 そこで,その技術力を磨くために,参考に近年話題になっているSAM(元論文URL)およびASAM(元論文URL)の実装について理解したいと思います。(アルゴリズムについてはこちらが参考になると思います。SoTAを総なめ!衝撃のオプティマイザー「SAM」爆誕&解説!by omilita)。 実装に関してはofficialなrepositoryはtensorflowで実装されていたこともあり,こちらのunofficialな実装のレポジトリを参照します(というのも,私自身pytorchの実装を最近はメインで行っているためです)。 また,SAMの親クラスOptimizerのgithubはこちらです. 今回私があまり見たことがないと感じたpythonの基本文法は dict.setdefault("key_name","key_value") super("name",self).__ init__ (param, defaults) means super().__ init__(param, defaults) です。 はじめに,基本的なことかもしれませんがpythonの更新について触れます。 0. pythonの更新 pythonの関数の引数については全て参照渡しです。 ただ, pythonの変数にはimmutable(変更不可 intなど)と mutable(変更可 lstなど)があり,それによって挙動が変わります。 このimmutableなものは変更できませんが,mutableなものは変更されます。 説明をしてもわかりづらいので例で見ます。 まず,mutableな例です。 class Tmp: def __init__(self,ora): self.ora=ora def change_class(clf): print(f"before : {id(clf), id(clf.ora)}") clf.ora=4 print(f" after : {id(clf), id(clf.ora)}") kk=Tmp(33) a=33 print(kk.ora,id(kk)) # 33 4493213312 change_class(kk) # before : (4493213312, 4491963696) # after : (4493213312, 4491962768) print(kk.ora) # 4 classはmutableのため,値を更新してもid(メモリのアドレス)は変更されません。 そのため値は更新されます(kk.oraが4に変わりました)。 ただ,classの属性はimmutableのためメモリの番地が変化します。 変化されてもclassのアドレスが変化しないため更新されます。 一方で,immutableな場合は値は更新されません。 ora=1 def add(a): print(f"before : {id(a)}") a+=1 print(f" after : {id(a)}") print(ora,id(ora)) # 1 4428745008 add(ora) # before : 4428745008 # after : 4428745040 print(ora) # 1 これがmutableとimmutableの違いです。 今回,pytorchのモデルはtorch.nn.Moduleを親クラスとして持つため,returnでモデルを返さずとも値は勝手に更新されます。 そのため,optimizer.step()をコードとして書くことによって値が更新されます。 では,実際のコードを見て行きましょう。 1. まず,SGDを見る 使用例としては以下のようなものがあります。 Example: ## Sets the optimizer. optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9) ## Sets the gradients of all optimized :class:`torch.Tensor` s to zero. optimizer.zero_grad() ## Computes the gradient of current tensor w.r.t. graph leaves. loss_fn(model(input), target).backward() ## Performs a single optimization step (parameter update). optimizer.step() SGDのコードはこちらになります。一部改変したのが下記です。 from torch.optim.optimizer import Optimizer from torch.optim.optimizer import required from torch.optim import _functional as F class SGD(Optimizer): # 初期化 def __init__(self, params, lr=required, momentum=0, dampening=0, weight_decay=0, nesterov=False, *, maximize=False): if lr is not required and lr < 0.0: raise ValueError("Invalid learning rate: {}".format(lr)) defaults = dict(lr=lr, momentum=momentum, dampening=dampening, weight_decay=weight_decay, nesterov=nesterov, maximize=maximize) super(SGD, self).__init__(params, defaults) # settingをする場合に用いる def __setstate__(self, state): super(SGD, self).__setstate__(state) for group in self.param_groups: group.setdefault('nesterov', False) group.setdefault('maximize', False) # ここの中での計算は微分を必要としない # closureは損失関数を再度評価するアルゴリズムの場合のみ用いる # 詳細は https://pytorch.org/docs/stable/optim.html @torch.no_grad() def step(self, closure=None): loss = None if closure is not None: # ここだけ微分を必要とする with torch.enable_grad(): loss = closure() for group in self.param_groups: params_with_grad = [] d_p_list = [] momentum_buffer_list = [] weight_decay = group['weight_decay'] momentum = group['momentum'] dampening = group['dampening'] nesterov = group['nesterov'] maximize = group['maximize'] lr = group['lr'] for p in group['params']: if p.grad is not None: params_with_grad.append(p) d_p_list.append(p.grad) state = self.state[p] if 'momentum_buffer' not in state: momentum_buffer_list.append(None) else: momentum_buffer_list.append(state['momentum_buffer']) # functional API that performs SGD algorithm computation. F.sgd(params_with_grad, d_p_list, momentum_buffer_list, weight_decay=weight_decay, momentum=momentum, lr=lr, dampening=dampening, nesterov=nesterov, maximize=maximize,) # update momentum_buffers in state for p, momentum_buffer in zip(params_with_grad, momentum_buffer_list): state = self.state[p] state['momentum_buffer'] = momentum_buffer return loss 上記がSGDのモデルです。APIの部分のコード(リンク)はこちらです。 基本的に torchのadd torchのmul torchのdetach で実装されています。 def sgd(params: List[Tensor], d_p_list: List[Tensor], momentum_buffer_list: List[Optional[Tensor]], *, weight_decay: float, momentum: float, lr: float, dampening: float, nesterov: bool, maximize: bool): for i, param in enumerate(params): d_p = d_p_list[i] if weight_decay != 0: d_p = d_p.add(param, alpha=weight_decay) if momentum != 0: buf = momentum_buffer_list[i] if buf is None: buf = torch.clone(d_p).detach() momentum_buffer_list[i] = buf else: buf.mul_(momentum).add_(d_p, alpha=1 - dampening) if nesterov: d_p = d_p.add(buf, alpha=momentum) else: d_p = buf alpha = lr if maximize else -lr param.add_(d_p, alpha=alpha) では本題に写ります。 2. SAMのコード 使用例は以下の通りです。 from sam import SAM ... model = YourModel() # define an optimizer for the "sharpness-aware" update base_optimizer = torch.optim.SGD optimizer = SAM(model.parameters(), base_optimizer, lr=0.1, momentum=0.9) ... for input, output in data: def closure(): loss = loss_function(output, model(input)) loss.backward() return loss loss = loss_function(output, model(input)) loss.backward() optimizer.step(closure) optimizer.zero_grad() ... SAMのコードはこちら です(一部改変)。 基本文法は torchのnorm torchのstack が基本です。 SAMはbaseとなるoptimizerを用意するため多少実装に工夫が入りますが, **kwargsを用いることでうまく実装できます。 class SAM(torch.optim.Optimizer): # Neighbo(rho)od size つまり rhoで論文中のρを再現 # AdaptiveでASAMという新規手法の実装をする def __init__(self, params, base_optimizer, rho=0.05, adaptive=False, **kwargs): assert rho >= 0.0, f"Invalid rho, should be non-negative: {rho}" defaults = dict(rho=rho, adaptive=adaptive, **kwargs) super(SAM, self).__init__(params, defaults) self.base_optimizer = base_optimizer(self.param_groups, **kwargs) self.param_groups = self.base_optimizer.param_groups # ここで,現時点での勾配からεを求める. @torch.no_grad() def first_step(self, zero_grad=False): grad_norm = self._grad_norm() for group in self.param_groups: scale = group["rho"] / (grad_norm + 1e-12) for p in group["params"]: if p.grad is None: continue # ここで現在のパラメータの場所を保持する self.state[p]["old_p"] = p.data.clone() e_w = (torch.pow(p, 2) if group["adaptive"] else 1.0) * p.grad * scale.to(p) p.add_(e_w) # climb to the local maximum "w + e(w)" if zero_grad: self.zero_grad() # ここで実際に更新をするための動きを加える @torch.no_grad() def second_step(self, zero_grad=False): for group in self.param_groups: for p in group["params"]: if p.grad is None: continue p.data = self.state[p]["old_p"] # get back to "w" from "w + e(w)" self.base_optimizer.step() # do the actual "sharpness-aware" update if zero_grad: self.zero_grad() # closureをもらい,更新する前のパラメータからの空間を考えるようにする。 @torch.no_grad() def step(self, closure=None): assert closure is not None, "Sharpness Aware Minimization requires closure, but it was not provided" closure = torch.enable_grad()(closure) # the closure should do a full forward-backward pass self.first_step(zero_grad=True) closure() # 勾配を更新する self.second_step() def _grad_norm(self,norm_dim=2): shared_device = self.param_groups[0]["params"][0].device # put everything on the same device, in case of model parallelism norm = torch.norm( torch.stack([ ((torch.abs(p) if group["adaptive"] else 1.0) * p.grad).norm(p=norm_dim).to(shared_device) for group in self.param_groups for p in group["params"] if p.grad is not None ]), p=norm_dim ) return norm 以上が実装です。 3. 最後に 当たり前かもしれませんが,新たな理論を構築した後に何かを実装したい場合は実装したいコードの基本的な文法を理解して実装する必要があると改めて感じました。 必要に応じてキャッチアップをして正しく可読性の高い実装と思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS初心者による実践AWSデータサイエンスのつまずいたメモ (途中)

はじめに ※この記事は現在書き途中になります。 学習の進捗に合わせて更新されていくかと思います。 この記事ではAWS初心者の私が「実践AWSデータサイエンス」の本を読んでつまずいたところをメモしていく記事となります。 こちらの書籍内容は求めていたものですが、省略が多く(実践なので)、AWSの基本的な使い方については書かれていないように思えます。そこで試行錯誤しながらゆっくりメモしていければと思います。 3章 SageMaker Autopilot データセットの取得について SageMaker Studioを起動して、以下のファイルを開く https://github.com/data-science-on-aws/oreilly_book/blob/master/03_automl/01_Prepare_Dataset_Autopilot.ipynb そのあとすべて実行することで作成できる PythonSDKでSageMakerAutopilotをしたときにS3にアクセスできない role/service-role/awsdeepracersagemakeraccessrole has "s3:putobject" permissions on the bucket. というエラーが発生。S3とSagemakerのフルアクセスをIAMに設定しているのにダメだった。 【機械学習初級】Amazon SageMaker導入の手順~S3にデータをアップロード編~ こちらの記事を発見して以下の内容を実施したところエラーが解決した(ただしSでなくsでないとエラーになる) S3バケットを作成するうえで、注意する点が1点あります。 それは、バケット名に「Sagemaker」を含めることです。 こうすることでノートブックインスタンスを作成する際に設定をしたデフォルトで設定されているIAMポリシーでSagemakerがS3バケットにアクセスする権限を付与することができます。 Athenaでmismatched input 'FUNCTION'. Expecting: 'EXTERNAL' Athenaのテーブル名に-を使っていたことが原因だった aws-handson → aws_hands に変更して対応した Athenaで結果が空白や文字列が数値になって返ってくる これは入力のS3とクエリの履歴保存先であるS3が同じになっているのが原因 入力のS3はファイルのみしか置いてはいけない エディタの設定から出力先のS3を設定して解決 おわりに 1ページ進めるのに数時間かかるこの本ですが500p以上の大ボリューム。果たして一通りやることができるのか不安で仕方ないです。 わからないことも多いですが、なんとか進めていければと思います。 本当にわからないところは問い合わせるのもアリかな 参考 【機械学習初級】Amazon SageMaker導入の手順~S3にデータをアップロード編~ Athena に mismatched input 'external'. expecting: 'or', 'schema', 'table', 'view' と言われた
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python初心者の備忘録~Pandas編~※2021/12/02更新

はじめに 社会人1年目のyappiです! 最近は仕事おわりにすぐさまpythonの勉強をしています! kaggleやpythonの学習を進める中で、よく使ったライブラリについてまとめておきます。 今回はPandasについてまとめます。今後も使用したメソッドについてはここに追記していきます。 Pandas kaggleの分析をする際に、1番初めに使用したPandasです! ファイルの読み込みの際には、欠かせないライブラリですよね。 まぁ、Udemyなどで学習をしている中でも様々なライブラリは出てきましたが...。 ファイルの読み込み train = pd.read_csv('train.csv') test = pd.read_csv('test.csv') 欠損値の穴埋め、削除 train['Fare'] = train['Fare'].fillna(train['Fare'].median()) train['Age'] = train['Age'].fillna(train['Age'].median()) train['Embarked'] = train['Embarked'].fillna('S') train.dropna(subset=['Fare']) train.dropna(subset=['Age']) train.dropna(subset=['Embarked']) 値の変換 train['Embarked'] = train['Embarked'].map({'S':0, 'C':1, 'Q':2}).astype(int) 平均値の取得 train.mean() 欠損値の抽出 train[‘Fare’].isnull() train[‘Age’].isnull() おわりに まだまだ使えるメソッドはあるかと思いますが、今回はTitanicを進めていく中で、使用したライブラリ、また、基本的なメソッドについてまとめました。 今後も新しいメソッドやデータの加工について調査する機会があればここに追記していきます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】株価データ分析 株価チャートを描く~mplfinance編 その2~

はじめに 【Python】株価データ分析 株価チャートを描く~mplfinance編 その1~では, mplfinance を使ってローソク足や移動平均線などを描いてみた 今回は,ボリンジャーバンド,MACD,RSIチャートを描いてみる 株価データ取得 まずは使用するライブラリのインポートとトヨタの株価データの取得を行う import numpy as np import pandas as pd import pandas_datareader.data as pdr import datetime from dateutil.relativedelta import relativedelta import matplotlib.pyplot as plt import mplfinance as mpf # 12か月チャート month = 12 # チャートの基本設定 kwargs = dict(type = 'candle', style = 'yahoo') ## starsandstripes, yahoo # 12か月前から本日までのデータを取得する ed = datetime.datetime.now() st = ed - relativedelta(months = month) # トヨタ df = pdr.DataReader('7203.T', 'yahoo', st, ed) トヨタの12か月分のデータが取れた ボリンジャーバンド,MACD,RSI を計算する関数を用意する 計算式は,次を参考にしています ボリンジャーバンド MONEX, Inc.より ±1σ = n日の移動平均 ± n日の標準偏差 ±2σ = n日の移動平均 ± n日の標準偏差 × 2 ±3σ = n日の移動平均 ± n日の標準偏差 × 3 <ポイント>価格がバンド内に収まる確率について - ボリンジャーバンドの±1σの範囲内に収まる確率 ⇒ 約68.3% - ボリンジャーバンドの±2σの範囲内に収まる確率 ⇒ 約95.4% - ボリンジャーバンドの±3σの範囲内に収まる確率 ⇒ 約99.7% def bollingerband(c, period): bbma = c.rolling(window=period).mean() ## 平均 bbstd = c.rolling(window=period).std() ## 標準偏差 bbh1 = bbma + bbstd * 1 bbl1 = bbma - bbstd * 1 bbh2 = bbma + bbstd * 2 bbl2 = bbma - bbstd * 2 bbh3 = bbma + bbstd * 3 bbl3 = bbma - bbstd * 3 return bbh1, bbl1, bbh2, bbl2, bbh3, bbl3 MACD MONEX, Inc.より MACD=短期EMA-長期EMA MACDシグナル=MACDのEMA MACDに用いられる移動平均は「単純移動平均(SMA)」ではなく,「指数平滑移動平均(EMA)」を使う EMAは直近の値動きをより反映するため,SMAと比較して値動きに敏感に反応すると考えられる <ポイント> パラメータ値は,短期EMAが12,長期EMAが26,MACDシグナルが9に設定する場合が多い ※ただし,銘柄ごとやマーケット状況に応じてパラメータ値の変更が必要 MACDとMACDシグナルのゴールデンクロスで買い,デッドクロスで売り ※ adjust=False については,pandas.Series.ewmを参照 def macd(c, n1, n2, ns): ema_short = c.ewm(span=n1,adjust=False).mean() ema_long = c.ewm(span=n2,adjust=False).mean() macd = ema_short - ema_long signal = macd.ewm(span=ns,adjust=False).mean() histogram = macd - signal histogramplus = histogram.where(histogram > 0, 0) histogramminus = histogram.where(histogram < 0, 0) return macd, signal, histogram, histogramplus, histogramminus RSI MONEX, Inc.より RS = (n日間の終値の上昇幅の平均) ÷ (n日間の終値の下落幅の平均) RSI = 100 -(100 ÷ (RS+1)) <ポイント> n(パラメータ値)は考案者であるJ.W.ワイルダー氏が最適とする 14(日足)と設定する場合が多い 他パラメータ値としては、日足では9日,22日,42日,52日,週足では9週,13週 ## RSI(指数平滑移動平均版) def rsi(c, period): diff = c.diff() #前日比 up = diff.copy() #上昇 down = diff.copy() #下落 up = up.where(up > 0, np.nan) #上昇以外はnp.nan down = down.where(down < 0, np.nan) #下落以外はnp.nan #upma = up.rolling(window=period).mean() #平均 #downma = down.abs().rolling(window=period).mean() #絶対値の平均 upma = up.ewm(span=period,adjust=False).mean() #平均 downma = down.abs().ewm(span=period,adjust=False).mean() #絶対値の平均 rs = upma / downma rsi = 100 - (100 / (1.0 + rs)) return rsi これでボリンジャーバンド,MACD,RSI を計算する関数の用意はできた チャートを描く 3つのチャートを描きたい - ローソク足+ボリンジャーバンド(-3σ~+3σ) - MACD(MACD,シグナル,ヒストグラム) - RSI(25%と75%に線を引く) GridSpec を使って,3行1列,高さ比が 3:1:1 のチャートを描くエリアを用意する # 高さの比を 3:1:1 で GridSpec を用意する fig = mpf.figure(figsize=(9.6, 9.6), style='starsandstripes') gs = fig.add_gridspec(3, 1, hspace=0, wspace=0, height_ratios=(3,1,1)) (ax1,ax2,ax3) = gs.subplots(sharex='col') mplfinance で複数のチャートを描く方法を調べたが, 今のところこの方法が良いと考えている subplots で得た,(ax1,ax2,ax3) は, それぞれ,ローソク足+ボリンジャーバンドのaxes,MACDのaxes,RSIのaxesで 後から線や色を塗るときに使える プロットを作成する際に,mplfinance.make_addplot を呼ぶ チャートごとにパラメータの辞書を用意しておく # ボリンジャーバンドは axes No.1 に描く bbargs = dict(ax=ax1, width=.5, linestyle='dashdot', color='black') # MACD は axes No.2 に描く macdargs = dict(ax=ax2, width=1, ylabel='MACD') # RSI は axes No.3 に描く rsiargs = dict(ax=ax3, width=1, ylabel='RSI') # プロットを作成する(ボリンジャーバンド,MACD,RSI) ap = [ mpf.make_addplot(bbh1, **bbargs), mpf.make_addplot(bbl1, **bbargs), mpf.make_addplot(bbh2, **bbargs), mpf.make_addplot(bbl2, **bbargs), mpf.make_addplot(bbh3, **bbargs), mpf.make_addplot(bbl3, **bbargs), mpf.make_addplot(macd_, **macdargs, color='blue'), mpf.make_addplot(macdsignal, **macdargs, color='orange'), mpf.make_addplot(histogramplus, **macdargs, color='red', type='bar'), mpf.make_addplot(histogramminus, **macdargs, color='green', type='bar'), mpf.make_addplot(rsi_, **rsiargs, color='blue') ] 塗りつぶしは,fill_between を呼ぶ x に x座標データ,y1 と y2 に y座標データを渡す その間の領域が塗りつぶされる # ボリンジャーバンド(axes=1)の間を塗りつぶす(色は適当) ax1.fill_between(x=range(0, len(df.index)), y1=bbh3.values, y2=bbl3.values, alpha=0.02, color='red') ax1.fill_between(x=range(0, len(df.index)), y1=bbh2.values, y2=bbl2.values, alpha=0.03, color='blue') ax1.fill_between(x=range(0, len(df.index)), y1=bbh1.values, y2=bbl1.values, alpha=0.04, color='yellow') # RSI(axes=3) の25%と75%に線を引く ax3.hlines(xmin=0, xmax=len(df.index), y=25, linewidth=1, color='red') ax3.hlines(xmin=0, xmax=len(df.index), y=75, linewidth=1, color='red') # ローソク足を描く,用意したプロットを渡す mpf.plot(df, ax=ax1, addplot=ap, style='starsandstripes', type='candle', xrotation=30, ylabel='Price') まとめ 以下を実行するとこのようなチャートが表示される import numpy as np import pandas as pd import pandas_datareader.data as pdr import datetime from dateutil.relativedelta import relativedelta import matplotlib.pyplot as plt import mplfinance as mpf # 12か月チャート month = 12 # チャートの基本設定 kwargs = dict(type = 'candle', style = 'yahoo') ## starsandstripes, yahoo # 12か月前から本日までのデータを取得する ed = datetime.datetime.now() st = ed - relativedelta(months = month) # トヨタ df = pdr.DataReader('7203.T', 'yahoo', st, ed) def bollingerband(c, period): bbma = c.rolling(window=period).mean() ## 平均 bbstd = c.rolling(window=period).std() ## 標準偏差 bbh1 = bbma + bbstd * 1 bbl1 = bbma - bbstd * 1 bbh2 = bbma + bbstd * 2 bbl2 = bbma - bbstd * 2 bbh3 = bbma + bbstd * 3 bbl3 = bbma - bbstd * 3 return bbh1,bbl1,bbh2,bbl2,bbh3,bbl3 def macd(c, n1, n2, ns): ema_short = c.ewm(span=n1,adjust=False).mean() ema_long = c.ewm(span=n2,adjust=False).mean() macd = ema_short - ema_long signal = macd.ewm(span=ns,adjust=False).mean() histogram = macd - signal histogramplus = histogram.where(histogram > 0, 0) histogramminus = histogram.where(histogram < 0, 0) return macd,signal,histogram,histogramplus,histogramminus def rsi(c, period): diff = c.diff() #前日比 up = diff.copy() #上昇 down = diff.copy() #下落 up = up.where(up > 0, np.nan) #上昇以外はnp.nan down = down.where(down < 0, np.nan) #下落以外はnp.nan #upma = up.rolling(window=period).mean() #平均 #downma = down.abs().rolling(window=period).mean() #絶対値の平均 upma = up.ewm(span=period,adjust=False).mean() #平均 downma = down.abs().ewm(span=period,adjust=False).mean() #絶対値の平均 rs = upma / downma rsi = 100 - (100 / (1.0 + rs)) return rsi # float 型に df['Open'] = df['Open'].astype(float) df['High'] = df['High'].astype(float) df['Low'] = df['Low'].astype(float) df['Close'] = df['Close'].astype(float) o = df['Open'] c = df['Close'] l = df['Low'] h = df['High'] ''' チャートを描く ''' # ボリンジャーバンド(移動平均25日線) bbh1,bbl1,bbh2,bbl2,bbh3,bbl3 = bollingerband(c, 25) # MACD(短期=12,長期=26,シグナル=9) macd_,macdsignal,histogram,histogramplus,histogramminus = macd(c,12,26,9) # RSI(14日) rsi_ = rsi(c, 14) # 高さの比を 3:1:1 で GridSpec を用意する fig = mpf.figure(figsize=(9.6, 9.6), style='starsandstripes') gs = fig.add_gridspec(3, 1, hspace=0, wspace=0, height_ratios=(3,1,1)) (ax1,ax2,ax3) = gs.subplots(sharex='col') # ボリンジャーバンドは axes No.1 に描く bbargs = dict(ax=ax1, width=.5, linestyle='dashdot', color='black') # MACD は axes No.2 に描く macdargs = dict(ax=ax2, width=1, ylabel='MACD') # RSI は axes No.3 に描く rsiargs = dict(ax=ax3, width=1, ylabel='RSI') # プロットを作成する(ボリンジャーバンド,MACD,RSI) ap = [ mpf.make_addplot(bbh1, **bbargs), mpf.make_addplot(bbl1, **bbargs), mpf.make_addplot(bbh2, **bbargs), mpf.make_addplot(bbl2, **bbargs), mpf.make_addplot(bbh3, **bbargs), mpf.make_addplot(bbl3, **bbargs), mpf.make_addplot(macd_, **macdargs, color='blue'), mpf.make_addplot(macdsignal, **macdargs, color='orange'), mpf.make_addplot(histogramplus, **macdargs, color='red', type='bar'), mpf.make_addplot(histogramminus, **macdargs, color='green', type='bar'), mpf.make_addplot(rsi_, **rsiargs, color='blue') ] # ボリンジャーバンド(axes=1)の間を塗りつぶす(色は適当) ax1.fill_between(x=range(0, len(df.index)), y1=bbh3.values, y2=bbl3.values, alpha=0.02, color='red') ax1.fill_between(x=range(0, len(df.index)), y1=bbh2.values, y2=bbl2.values, alpha=0.03, color='blue') ax1.fill_between(x=range(0, len(df.index)), y1=bbh1.values, y2=bbl1.values, alpha=0.04, color='yellow') # RSI(axes=3) の25%と75%に線を引く ax3.hlines(xmin=0, xmax=len(df.index), y=25, linewidth=1, color='red') ax3.hlines(xmin=0, xmax=len(df.index), y=75, linewidth=1, color='red') # ローソク足を描く,用意したプロットを渡す mpf.plot(df, ax=ax1, addplot=ap, style='starsandstripes', type='candle', xrotation=30, ylabel='Price') mpf.show() おわりに 今回は,ボリンジャーバンド,MACD,RSIチャートを描いた 次回は,一目均衡表を描きたい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

scikit-learn使用時に出た'SVC' object has no attribute '_probA' エラーの対処法

はじめに scikit-learnを用いて機械学習させたモデルを、学習させたPCと違うPCでモデルを使用しようとしたところ、エラーが出た。 学習させたPCをPC①、使用しようとしたPCを②とする。 結論から言うとバージョンの違いのせいだったため、学習環境にバージョンを合わせれば問題ない。 ただし、筆者の環境においてはエラーが消えたが、必ずこれで消えるという訳ではない。 原因 はじめに の中で既に書いたが、scikit-learnのバージョン差によってエラーが出ていた。 バージョンを揃えればエラーを解消できる。 筆者の環境では、バージョンが以下のようになっていた。 PC①:0.23.2 PC②:0.22.2 解決までの手順 バージョン確認 それぞれのPCでscikit-learnのバージョンを確認する バージョン確認には以下のコードをコマンドプロンプトで使用する。 python import sklearn print (sklearn.__version__) 使用環境のバージョンを変える PC②で、以下のコードをコマンドプロンプトで使用する。 なお、PC②で別のモデルを作成した場合は、そのモデルはPC②で使用不可になるため注意が必要。 pip uninstall scikit-learn アンインストールが確認できた後、以下のコードを入力する。 ただし、0.22.2についてはPC②のバージョンを入れる必要がある。 pip install scikit-learn==0.22.2 以上の操作でエラーが消えた。 終わりに この記事では筆者の環境で発生したエラーの解決方法であり、他の環境すべてで対応できる保証はないが、 同様の対応でエラーが解消されることを期待する。 参考 https://algorithm.joho.info/machine-learning/python-scikit-learn-version/ https://www.grisoluto.com/entry/python-library-downgread
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【日付回文】本日、2021年12月02日は、逆から読んでも2021年12月02日

はじめに 本日、2021年12月02日をYYYYMMDDの形式で表すと、20211202です。 これを逆から読んでも、20211202になります。 つまり日付回文の日です。 @minorun365 が教えてくれました。 これがどれくらいあるか興味があったのでPythonでコードを書いて調べてみました。 コード なお、西暦1000年1月1日から数えて3000年分(およそ1095000日)としました。 import datetime dt = datetime.date(1000,1,1) i = 1 while i < 1095000: dt = dt + datetime.timedelta(days=1) str_date = str(dt).replace('-', '') yyyy = str_date[:4] mmdd = str_date[4:] r_mmdd = mmdd[::-1] if yyyy == r_mmdd: print("日付回文になる日は" + yyyy +"年"+ mmdd[:2] +"月"+ mmdd[2:] + "日です") else: i = i + 1 実行結果 日付回文になる日は1001年10月01日です 日付回文になる日は1010年01月01日です 日付回文になる日は1011年11月01日です 日付回文になる日は1020年02月01日です 日付回文になる日は1021年12月01日です 日付回文になる日は1030年03月01日です 日付回文になる日は1040年04月01日です 日付回文になる日は1050年05月01日です 日付回文になる日は1060年06月01日です 日付回文になる日は1070年07月01日です 日付回文になる日は1080年08月01日です 日付回文になる日は1090年09月01日です 日付回文になる日は1101年10月11日です 日付回文になる日は1110年01月11日です 日付回文になる日は1111年11月11日です 日付回文になる日は1120年02月11日です 日付回文になる日は1121年12月11日です 日付回文になる日は1130年03月11日です 日付回文になる日は1140年04月11日です 日付回文になる日は1150年05月11日です 日付回文になる日は1160年06月11日です 日付回文になる日は1170年07月11日です 日付回文になる日は1180年08月11日です 日付回文になる日は1190年09月11日です 日付回文になる日は1201年10月21日です 日付回文になる日は1210年01月21日です 日付回文になる日は1211年11月21日です 日付回文になる日は1220年02月21日です 日付回文になる日は1221年12月21日です 日付回文になる日は1230年03月21日です 日付回文になる日は1240年04月21日です 日付回文になる日は1250年05月21日です 日付回文になる日は1260年06月21日です 日付回文になる日は1270年07月21日です 日付回文になる日は1280年08月21日です 日付回文になる日は1290年09月21日です 日付回文になる日は1301年10月31日です 日付回文になる日は1310年01月31日です 日付回文になる日は1321年12月31日です 日付回文になる日は1330年03月31日です 日付回文になる日は1350年05月31日です 日付回文になる日は1370年07月31日です 日付回文になる日は1380年08月31日です 日付回文になる日は2001年10月02日です 日付回文になる日は2010年01月02日です 日付回文になる日は2011年11月02日です 日付回文になる日は2020年02月02日です 日付回文になる日は2021年12月02日です 日付回文になる日は2030年03月02日です 日付回文になる日は2040年04月02日です 日付回文になる日は2050年05月02日です 日付回文になる日は2060年06月02日です 日付回文になる日は2070年07月02日です 日付回文になる日は2080年08月02日です 日付回文になる日は2090年09月02日です 日付回文になる日は2101年10月12日です 日付回文になる日は2110年01月12日です 日付回文になる日は2111年11月12日です 日付回文になる日は2120年02月12日です 日付回文になる日は2121年12月12日です  <<< ★★★ 今日 ★★★ 日付回文になる日は2130年03月12日です  <<< ★★★ 次回 ★★★ 日付回文になる日は2140年04月12日です 日付回文になる日は2150年05月12日です 日付回文になる日は2160年06月12日です 日付回文になる日は2170年07月12日です 日付回文になる日は2180年08月12日です 日付回文になる日は2190年09月12日です 日付回文になる日は2201年10月22日です 日付回文になる日は2210年01月22日です 日付回文になる日は2211年11月22日です 日付回文になる日は2220年02月22日です 日付回文になる日は2221年12月22日です 日付回文になる日は2230年03月22日です 日付回文になる日は2240年04月22日です 日付回文になる日は2250年05月22日です 日付回文になる日は2260年06月22日です 日付回文になる日は2270年07月22日です 日付回文になる日は2280年08月22日です 日付回文になる日は2290年09月22日です 日付回文になる日は3001年10月03日です 日付回文になる日は3010年01月03日です 日付回文になる日は3011年11月03日です 日付回文になる日は3020年02月03日です 日付回文になる日は3021年12月03日です 日付回文になる日は3030年03月03日です 日付回文になる日は3040年04月03日です 日付回文になる日は3050年05月03日です 日付回文になる日は3060年06月03日です 日付回文になる日は3070年07月03日です 日付回文になる日は3080年08月03日です 日付回文になる日は3090年09月03日です 日付回文になる日は3101年10月13日です 日付回文になる日は3110年01月13日です 日付回文になる日は3111年11月13日です 日付回文になる日は3120年02月13日です 日付回文になる日は3121年12月13日です 日付回文になる日は3130年03月13日です 日付回文になる日は3140年04月13日です 日付回文になる日は3150年05月13日です 日付回文になる日は3160年06月13日です 日付回文になる日は3170年07月13日です 日付回文になる日は3180年08月13日です 日付回文になる日は3190年09月13日です 日付回文になる日は3201年10月23日です 日付回文になる日は3210年01月23日です 日付回文になる日は3211年11月23日です 日付回文になる日は3220年02月23日です 日付回文になる日は3221年12月23日です 日付回文になる日は3230年03月23日です 日付回文になる日は3240年04月23日です 日付回文になる日は3250年05月23日です 日付回文になる日は3260年06月23日です 日付回文になる日は3270年07月23日です 日付回文になる日は3280年08月23日です 日付回文になる日は3290年09月23日です ということで、次回は2130年03月12日(21300312)になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【日付回文】本日、2021年12月02日は、逆から読んでも2021年12月02日。次回はいつなのかPythonでコードを書いて調べてみた。

はじめに 本日、2021年12月02日をYYYYMMDDの形式で表すと、20211202です。 これを逆から読んでも、20211202になります。 つまり日付回文の日です。 @minorun365 が教えてくれました。 これがどれくらいあるか興味があったのでPythonでコードを書いて調べてみました。 コード なお、西暦1000年1月1日から数えて3000年分(およそ1095000日)としました。 import datetime dt = datetime.date(1000,1,1) i = 1 while i < 1095000: dt = dt + datetime.timedelta(days=1) str_date = str(dt).replace('-', '') yyyy = str_date[:4] mmdd = str_date[4:] r_mmdd = mmdd[::-1] if yyyy == r_mmdd: print("日付回文になる日は" + yyyy +"年"+ mmdd[:2] +"月"+ mmdd[2:] + "日です") else: i = i + 1 実行結果 日付回文になる日は1001年10月01日です 日付回文になる日は1010年01月01日です 日付回文になる日は1011年11月01日です 日付回文になる日は1020年02月01日です 日付回文になる日は1021年12月01日です 日付回文になる日は1030年03月01日です 日付回文になる日は1040年04月01日です 日付回文になる日は1050年05月01日です 日付回文になる日は1060年06月01日です 日付回文になる日は1070年07月01日です 日付回文になる日は1080年08月01日です 日付回文になる日は1090年09月01日です 日付回文になる日は1101年10月11日です 日付回文になる日は1110年01月11日です 日付回文になる日は1111年11月11日です 日付回文になる日は1120年02月11日です 日付回文になる日は1121年12月11日です 日付回文になる日は1130年03月11日です 日付回文になる日は1140年04月11日です 日付回文になる日は1150年05月11日です 日付回文になる日は1160年06月11日です 日付回文になる日は1170年07月11日です 日付回文になる日は1180年08月11日です 日付回文になる日は1190年09月11日です 日付回文になる日は1201年10月21日です 日付回文になる日は1210年01月21日です 日付回文になる日は1211年11月21日です 日付回文になる日は1220年02月21日です 日付回文になる日は1221年12月21日です 日付回文になる日は1230年03月21日です 日付回文になる日は1240年04月21日です 日付回文になる日は1250年05月21日です 日付回文になる日は1260年06月21日です 日付回文になる日は1270年07月21日です 日付回文になる日は1280年08月21日です 日付回文になる日は1290年09月21日です 日付回文になる日は1301年10月31日です 日付回文になる日は1310年01月31日です 日付回文になる日は1321年12月31日です 日付回文になる日は1330年03月31日です 日付回文になる日は1350年05月31日です 日付回文になる日は1370年07月31日です 日付回文になる日は1380年08月31日です 日付回文になる日は2001年10月02日です 日付回文になる日は2010年01月02日です 日付回文になる日は2011年11月02日です 日付回文になる日は2020年02月02日です 日付回文になる日は2021年12月02日です  <<< ★★★ 今日 ★★★ 日付回文になる日は2030年03月02日です  <<< ★★★ 次回 ★★★ 日付回文になる日は2040年04月02日です 日付回文になる日は2050年05月02日です 日付回文になる日は2060年06月02日です 日付回文になる日は2070年07月02日です 日付回文になる日は2080年08月02日です 日付回文になる日は2090年09月02日です 日付回文になる日は2101年10月12日です 日付回文になる日は2110年01月12日です 日付回文になる日は2111年11月12日です 日付回文になる日は2120年02月12日です 日付回文になる日は2121年12月12日です 日付回文になる日は2130年03月12日です 日付回文になる日は2140年04月12日です 日付回文になる日は2150年05月12日です 日付回文になる日は2160年06月12日です 日付回文になる日は2170年07月12日です 日付回文になる日は2180年08月12日です 日付回文になる日は2190年09月12日です 日付回文になる日は2201年10月22日です 日付回文になる日は2210年01月22日です 日付回文になる日は2211年11月22日です 日付回文になる日は2220年02月22日です 日付回文になる日は2221年12月22日です 日付回文になる日は2230年03月22日です 日付回文になる日は2240年04月22日です 日付回文になる日は2250年05月22日です 日付回文になる日は2260年06月22日です 日付回文になる日は2270年07月22日です 日付回文になる日は2280年08月22日です 日付回文になる日は2290年09月22日です 日付回文になる日は3001年10月03日です 日付回文になる日は3010年01月03日です 日付回文になる日は3011年11月03日です 日付回文になる日は3020年02月03日です 日付回文になる日は3021年12月03日です 日付回文になる日は3030年03月03日です 日付回文になる日は3040年04月03日です 日付回文になる日は3050年05月03日です 日付回文になる日は3060年06月03日です 日付回文になる日は3070年07月03日です 日付回文になる日は3080年08月03日です 日付回文になる日は3090年09月03日です 日付回文になる日は3101年10月13日です 日付回文になる日は3110年01月13日です 日付回文になる日は3111年11月13日です 日付回文になる日は3120年02月13日です 日付回文になる日は3121年12月13日です 日付回文になる日は3130年03月13日です 日付回文になる日は3140年04月13日です 日付回文になる日は3150年05月13日です 日付回文になる日は3160年06月13日です 日付回文になる日は3170年07月13日です 日付回文になる日は3180年08月13日です 日付回文になる日は3190年09月13日です 日付回文になる日は3201年10月23日です 日付回文になる日は3210年01月23日です 日付回文になる日は3211年11月23日です 日付回文になる日は3220年02月23日です 日付回文になる日は3221年12月23日です 日付回文になる日は3230年03月23日です 日付回文になる日は3240年04月23日です 日付回文になる日は3250年05月23日です 日付回文になる日は3260年06月23日です 日付回文になる日は3270年07月23日です 日付回文になる日は3280年08月23日です 日付回文になる日は3290年09月23日です ということで、次回は2030年03月02日(20300302)になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

逆翻訳を使って日本語テキストデータを拡張(水増し)してみた

はじめに こんにちは、NTTドコモ サービスイノベーション部の稲子です。 本記事は、NTTドコモR&D Advent Calendar 2021の4日目の記事です。 普段の業務ではチャットボットの開発・運用に携わっています。 今回は、逆翻訳によるテキストデータの拡張方法の紹介と文書分類タスクでの実験をしていきたいと思います。 逆翻訳とは 逆翻訳は言語処理分野でのデータ拡張 (Data Augmentation) の手法の1つです。 データ拡張とは、既存のデータに処理を行い擬似データを生成するテクニックで、深層学習モデルの作成で大量の学習データが必要な場合によく用いられます。 逆翻訳では、下の図のように元の文を他言語で翻訳しその翻訳文を元の言語で翻訳し直すことで、元の文の意味は保ったまま表現の言い換えを行います。 1. 逆翻訳でデータを水増ししてみる 本記事では、翻訳APIを用いて手軽に逆翻訳する方法をご紹介します。 それでは実際にやってみましょう。 データの準備 今回はリクルートが提供するじゃらんnetに投稿されたポジネガラベル付きの宿クチコミのコーパスを使用します。 コーパスのサンプルはこちらです。 テキスト ラベル  ラベル(変換後) 出張でお世話になりました。 0 0 また是非行きたいです。 1 1 残念...。 -1 2 テキストとラベルで構成されており、テキストが宿のクチコミで、ラベルは1がポジティブ、0が中立、-1がネガティブになっています。 ラベルに-1が付与されていると実験時に利用する分類器の学習時にIndexError: Target -1 is out of bounds.というエラーが出るので、今回はラベルが-1の場合は2に変換します。 まずはこちらを学習データと評価データに分けていきます。 import pandas as pd import numpy as np import random data = pd.read_csv("pn.tsv",sep='\t', header=None) sentences = list(data[2]) labels = list(data[1].replace(-1, 2)) dataset = [] for sent, label in zip(sentences, labels): dataset.append([sent, label]) train_size = int(0.9 * len(data)) np.random.shuffle(dataset) train_data = dataset[:train_size] test_data = dataset[train_size:] train = open("train.tsv", mode = "w") test = open("test.tsv", mode = "w") for sent, label in train_data: train.write(sent+"\t"+str(label)+"\n") for sent, label in test_data: test.write(sent+"\t"+str(label)+"\n") 上記のスクリプトを実行すると、下記のようなタブ区切りでテキストとラベルが並んだtrain.tsvとtest.tsvというファイルが出力されます。 train.tsvが学習データ、test.tsvが評価データになります。 ご飯とお味噌汁がセルフなので、そちらで調整かな。 0 駅に近いです。 1 各データのラベルごとの文数はこちらです。 ラベル 学習データ 評価データ 0 1209 120 1 3047 359 2 741 77 計 4997 556 逆翻訳 次に学習データのテキストを逆翻訳して拡張データを作成していきます。今回はみらい翻訳APIを利用します。 下記のスクリプトのtranslate_by_mirai関数でみらい翻訳APIを呼び出して、翻訳された結果を取得しています。 "hoge"には取得したAPIキーを入力します。 import pandas as pd API_KEY = "hoge" train = open("train_bt.tsv", mode = "w") data = pd.read_csv("train.tsv",sep='\t', header=None) sentences = list(data[0]) labels = list(data[1]) for sent, label in zip(sentences, labels): translated_en = translate_by_mirai(sent, API_KEY) # APIの形式は省略します translated_ja = translate_by_mirai(translated_en, API_KEY)# APIの形式は省略します if sent != translated_ja: train.write(translated_ja + "\t" + str(label) + "\n") スクリプトを実行すると、train.tsvのテキストを逆翻訳した結果とテキストと元の文の対になっていたラベルが並んだtrain_bt.tsvという拡張データが生成できます。 ご飯と味噌汁はセルフサービスなので、そこで調整できると思います。 0 駅の近くです。 1 逆翻訳した結果が元の文と同じになるような場合は、train_bt.tsvに出力していません。 今回の場合は、元の学習データのテキストが4997文に対して拡張データのテキストは4423文となり、元のデータの89%を拡張することができました。 元の文と翻訳した結果の例はこちらです。 逆翻訳文では、元の文とは異なる語順や別の表現の単語が生成できている事がわかります。 元の文が短い場合は、元の文→訳文、訳文→逆翻訳文のそれぞれの翻訳は正しくても、元の文と逆翻訳文の意味が異なるものも見受けられます。 元の文 訳文 逆翻訳文(拡張文) ラベル  部屋は特に不満はありませんでした。 I didn't have any complaints about the room. お部屋に不満はありませんでした。 0 リベンジします。 I'll take revenge. 復讐します。 0 和食を選んだのですが、洋食はハムと玉子を注文してから作っていたので、何か妙に美味しそうに見えました。 I chose Japanese food, but I made Western food after ordering ham and eggs, so it looked strangely delicious. 和食を選びましたが、ハムと卵を注文してから洋食を作ったので、不思議と美味しそうでした。 1 リピート決定です。 I decided to repeat it. 私はそれを繰り返すことにした。 1 お風呂も良かったのですが、露天風呂が3階の風呂にしか無く少し残念でした。 The bath was good, but it was a little disappointing that the open-air bath was only in the bath on the third floor. お風呂は良かったのですが、露天風呂が3階のお風呂しかないのが少し残念でした 2 本当に残念です。 That's too bad. それはいけませんね。 2 2. 水増ししたデータをBERT分類器に学習させて文書分類してみる 上記で作成した学習データと拡張データを合わせて BERT を用いた分類器に学習させ、文書分類タスクで実験してみます。 今回は BERT の事前学習モデルとして東北大学の乾研究室が作成したbert-base-japanese-whole-word-maskingを使用しました。 文書分類のスクリプトはこちらを参考にさせていただきました。 データセットクラスの作成 まずは分類器の学習データとなるtrain.tsvとtrain_bt.tsvのテキストと評価データとなるtest.tsvのテキストをBERTトークナイザで解析し、出力結果と各データのラベルをテンソルに変換して学習データセットと評価データセットを作成します。 import pandas as pd import torch from torch.utils.data import TensorDataset, random_split from torch.utils.data import DataLoader, RandomSampler, SequentialSampler from tqdm import tqdm from transformers import BertJapaneseTokenizer from transformers import BertForSequenceClassification, AdamW, BertConfig def load_file(file): data = pd.read_csv(file, sep='\t', header=None, engine='python') sentences = list(data[0]) labels = list(data[1]) dataset = [] for sent, label in zip(sentences, labels): dataset.append([sent, label]) return dataset def preprocess(data): sentences = [] labels = [] for sent, label in data: sentences.append(sent) labels.append(label) data = sentences, labels tokenizer = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking') input_ids = [] attention_masks = [] max_length = 100 # データセットの準備 for sent in sentences: encoded_dict = tokenizer.encode_plus( sent, add_special_tokens = True, # Special Tokenの追加 truncation=True, max_length = max_length, # 文章の長さを固定(Padding/Trancatinating) pad_to_max_length = True,# PADDINGで埋める return_attention_mask = True, # Attention maksの作成 return_tensors = 'pt', # Pytorch tensorsで返す ) # 単語IDを取得 input_ids.append(encoded_dict['input_ids']) # Attention maskの取得 attention_masks.append(encoded_dict['attention_mask']) # リストに入ったtensorを縦方向(dim=0)へ結合 input_ids = torch.cat(input_ids, dim=0) attention_masks = torch.cat(attention_masks, dim=0) # tenosor型に変換 labels = torch.tensor(labels) return input_ids, attention_masks, labels train_data = load_file("train.tsv") train_bt_data = load_file("train_bt.tsv") val_data = load_file("test.tsv") bt_input_ids, bt_attention_masks, bt_labels = preprocess(train_data+train_bt_data) v_input_ids, v_attention_masks, v_labels = preprocess(val_data) # データセットクラスの作成 train_bt_dataset = TensorDataset(bt_input_ids, bt_attention_masks, bt_labels) val_dataset = TensorDataset(v_input_ids, v_attention_masks, v_labels) 拡張後の学習データの各ラベルの文数はこちらです。 ラベル 学習データ 0 2273 (+1064) 1 5710 (+2663) 2 1437 (+696) 計 9420 (+4423) 学習 次に、学習データセットを分類器に学習させます。 分類器は Huggingface のBertForSequenceClassificationを利用しました。 def train(data_loader, model): # 最適化手法の設定 optimizer = AdamW(model.parameters(), lr=2e-5) model.train() # 訓練モードで実行 train_loss = 0 for batch in tqdm(data_loader):# train_dataloaderはword_id, mask, labelを出力 b_input_ids = batch[0].to(device) b_input_mask = batch[1].to(device) b_labels = batch[2].to(device) optimizer.zero_grad() outputs = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask, labels=b_labels) loss = outputs.loss loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() train_loss += loss.item() return train_loss # データローダーの作成 batch_size = 8 # 訓練データローダー train_bt_dataloader = DataLoader( train_bt_dataset, sampler = RandomSampler(train_bt_dataset), # ランダムにデータを取得してバッチ化 batch_size = batch_size ) # 検証データローダー validation_dataloader = DataLoader( val_dataset, sampler = SequentialSampler(val_dataset), # 順番にデータを取得してバッチ化 batch_size = batch_size ) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") # BertForSequenceClassification 学習済みモデルのロード model = BertForSequenceClassification.from_pretrained( "cl-tohoku/bert-base-japanese-whole-word-masking", # 日本語Pre trainedモデルの指定 num_labels = 3, # ラベル数 output_attentions = False, # アテンションベクトルを出力するか output_hidden_states = True, # 隠れ層を出力するか ) # 学習の実行 max_epoch = 5 train_loss_ = [] test_loss_ = [] for epoch in tqdm(range(max_epoch)): train_ = train(data_loader, model) log_metric("train_loss", train_, step=epoch) 評価 最後に分類器に評価データを入力して出力される予測ラベルと正解ラベルを照らし合わせ、分類器の予測性能の評価をします。 y_true = [] y_pred = [] model.eval()# 訓練モードをオフ for batch in validation_dataloader: b_input_ids = batch[0].to(device) b_input_mask = batch[1].to(device) b_labels = batch[2].to(device) y_true.extend(b_labels.cpu().numpy()) with torch.no_grad(): # 学習済みモデルによる予測結果をpredsで取得 preds = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask) y_pred.extend(np.argmax(preds[0].cpu().numpy(), axis=1)) from sklearn.metrics import classification_report with open("output.txt", "w") as f: f.write(classification_report(y_true, y_pred, digits=4)) 結果 元の文だけでも結構高い精度ですが、逆翻訳でデータを拡張したことでより全体の精度が上がりました。 中でも元のデータ量が少なかったラベル2では、全ての値が大きく向上しました。 このことから、逆翻訳によるデータ拡張は、データ量が少ない場合に対して特に有効であると考えられます。 元の文のみを学習した場合 Accuracy:0.8579 ラベル precision recall  f1-score 0 0.6953 0.7417 0.7177 1 0.9280 0.8969 0.9122 2 0.7619 0.8312 0.7950 元の文と拡張文を合わせて学習した場合 Accuracy:0.8705 ラベル precision recall  f1-score 0 0.7545 0.6917 0.7217 1 0.9278 0.9304 0.9291 2 0.8148 0.8571 0.8354 まとめ 逆翻訳を用いて日本語コーパスのデータ拡張を実施し、文書分類タスクに適用してみました。 今回は日→英→日のみの逆翻訳を試しましたが、他の言語でも翻訳することでよりデータのバリエーションを増やせるかと思います。 参考文献 1.https://amitness.com/2020/05/data-augmentation-for-nlp/ 2.https://www.nogawanogawa.com/entry/rte 3.https://qiita.com/nena0undefined/items/c2926bad07039e5540ab
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyQt5のチュートリアルを動かす ⑫ ~ QPixmapClassについて

はじめに QPixmapクラスはQPaintDeviceオブジェクトとして使用したり、他のウィジェット(ラベルやボタン等)に読み込ませることができます。 Qt APIには,I/Oやその他のピクセル操作に最適化された類似のクラスにQImageがあります。一方Pixmapは画面に表示するために最適されています。これらは相互に変換も可能です。 QPixmapオブジェクトに読み込むことができる画像形式 BMP Windows Bitmap GIF Graphic Interchange Format (optional) JPG Joint Photographic Experts Group JPEG Joint Photographic Experts Group PNG Portable Network Graphics PBM Portable Bitmap PGM Portable Graymap PPM Portable Pixmap XBM X11 Bitmap XPM X11 Pixmap QPixmapオブジェクの操作に便利なメソッドたち copy() QRectオブジェクトからpixmapデータをコピー fromImage() QImageオブジェクトをQPixmapに変換 grabWidget() 与えられたウィジェットからpixmapを生成 Load() イメージファイルをpixmapとしてロード save() QPixmapオブジェクトをファイルとして保存 toImage() QPixmapをQImageへ変換 ※QPixmapの最も一般的な使い方は,ラベルやボタンに画像を表示することだそうです デモ pixmap.py import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * def window(): app = QApplication(sys.argv) win = QWidget() l1 = QLabel() l1.setPixmap(QPixmap("test.png")) vbox = QVBoxLayout() vbox.addWidget(l1) win.setLayout(vbox) win.setWindowTitle("QPixmap Demo") win.show() sys.exit(app.exec_()) if __name__ == '__main__': window() 実行結果
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

!!初心者必見!!今だからわかるプログラミングをやり始めて知った専門用語集

はじめに 初めまして!私は今年の春からプログラミングを始めた納豆と申します!!自分がプログラミングを始めた際と比べて自分の中のプログラミング用語集が増えてきたので今回はこれからプログラミングを始める方向けに、今後聞く機会が増えるであろう単語集を紹介したいと思います。この記事が少しでも読んでくださった方の役に立てば幸いです。 1.github githubを使うことによって、エンジニアが書いたコードをローカルではなく、WEB上で保存することができます。また、もし新しいコードを書いたけど、前に書いたコードに戻したいって思ったときに、githubを使えば、過去のコードをみれたり、また、チームで開発する際に使えたりします。 2.デプロイ デプロイとは、簡単に言うと、つくったWEBアプリやWEBサイトをネットに公開することです。 3.ソースコード コンピュータのプログラムやアプリケーションソフトウェアを動作させるための文字列のことです。この下のおまけに書かれているのがまさにソースコードです。 4.コンパイル 簡単に言うと、人間が書いたソースコードを機械語に翻訳することです。 5.デバッグ 簡単に言うとバグを見つけてそれを修正することです。 6.ローコード・ノーコード開発 ローコード開発とはコードを最小限しか書かずにアプリなどを開発する開発方法で、ノーコード開発は名前の通り、プログラミングをせずに開発する開発方法です。 おまけ さまざまなプログラミング言語 HTML/CSS これはWEBサイトを製作する際に使われる言語で、HTMLという言語でWEBページの文字や画像、アイコンなどを表示させることができ、それをCSSという言語で色を付けたり、文字の大きさをつけたりするといった感じです。 index.html <h1>こんにちは</h1> htmlで表示した文字をcssで、色を付けたり、背景に色を付けたりすることができます style.css .h1{ color :blue; background-color :green; } Python Pythonというプログラミング言語は、現在かなり、人気がある言語として取り扱われています。主にPythonで作れるものとしては、最近はやりのAI(人工知能)、WEBサイト、ゲーム、業務自動化ツールなど、Pythonという言語を勉強しておけば、かなりのものを作ることができます。比較的学びやすいという点もPythonのいい点です。 python print("こんにちは") javascript こちらの言語は主にWEBサイトやWEBアプリを作る際に使われます。WEBサイトを作る際にこの言語を使うと、サーバーと通信して情報を送ったり、受信できたり、HTMLやCSSで書かれた画面に動きを与えることができたり、様々なことが可能です。 javascript console.log("こんにちは") C言語 こちらの言語は習得するのが難しいかわりに、WEBサイト、WEBアプリ、ゲームの開発ができ、これに加え、システムの開発や家電などの家電に組み込むシステム、ロボットなども作ることができます c #include <stdio.h> int main() { printf("こんにちは"); return 0; } Java こちらの言語でほかの言語と同じようにWEBサイト、WEBアプリ、ゲームの開発ができ、これに加え、システムの開発や家電などの家電に組み込むシステムも作ることができます。また、この言語はオブジェクト指向という概念に基づいています。 java public class Hello{ public static void main(String argv[]){ System.out.println("こんにちは"); } } Ruby ruby String.new("Hello") ・Unity こちらの言語はゲーム制作に特化した言語であり、2D、3Dのゲームを作ることができ、なんとあのポケモンGOもUnityで作られています。 さいごに いかがだったでしょうか。途中から用語というより言語の紹介になってしまった気がしますが、もしお役に立てたなら幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyQt5のチュートリアルを動かす ⑪ ~ 描画関数について

はじめに すべてのQWidgetクラスは,QPaintDeviceクラスのサブクラスです。QPaintDeviceはQPainterを使って描画します。そしてペイントデバイスのサイズは左上隅からのピクセル単位で決まります。 QPainterクラスはウィジェットやプリンタなどのペイント可能なデバイスに対して低レベルのペイントを行います。通常はウィジェットのペイントイベントで使用されます。QPaintEventはウィジェットの外観が更新されるたびに発生します。 ペインターはbegin()メソッドを呼ぶことでアクティブになり、end()メソッドで非アクティブになります。その間色々なメソッドを使って希望するパターンをペイントできます。 描画メソッド一覧 1.begin() 対象のデバイスのペイントを開始 2.drawArc() 開始と終了角度間の円弧を描画 3.drawEllipse() 矩形の中に楕円を描く(短径を指定して、円を描画できる?) 4.drawLine() エンドポイント座標を指定して線を描画 5.drawPixmap() 画像ファイルからpixmapを抽出し、指定位置に表示する 6.drwaPolygon() 座標配列による多角形を描画 7.drawRect() 左上の座標と横、縦のサイズ指定して長方形を描画 8.drawText() 与えられた座標にテキストを描画 9.fillRect() 短径をQColorパラメータで塗りつぶす 10.setBrush() 描画用のブラシのスタイルを設定 11.setPen() 描画に使用するペンの色、スタイルを設定 デモ 以下のデモを実行すると色々なメソッドを試すことができます。 drawing.py import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * class Example(QWidget): def __init__(self): super(Example, self).__init__() self.initUI() def initUI(self): self.text = "hello world" self.setGeometry(100,100, 400,300) self.setWindowTitle('Draw Demo') self.show() def paintEvent(self, event): qp = QPainter() qp.begin(self) qp.setPen(QColor(Qt.red)) qp.setFont(QFont('Arial', 20)) qp.drawText(10,50, "hello Python") qp.setPen(QColor(Qt.blue)) qp.drawLine(10,100,100,100) qp.drawRect(10,150,150,100) qp.setPen(QColor(Qt.yellow)) qp.drawEllipse(100,50,100,50) qp.drawPixmap(220,10,QPixmap("test.png")) qp.fillRect(20,175,130,70,QBrush(Qt.SolidPattern)) qp.end() def main(): app = QApplication(sys.argv) ex = Example() sys.exit(app.exec_()) if __name__ == '__main__': main() 実行すると以下のようになりました。 さいごに 個人的に描画した図形をドラッグ&ドロップしたりする方法が気になります。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyQt5のチュートリアルを動かす ➉ ドラッグ&ドロップ

はじめに PyQt5でのドラッグ&ドロップです ドラッグ&ドロップ機能はユーザーにとって直感的で使いやすいため、多くのデスクトップアプリの機能に用意されています。 QDragクラスを使用するとそのような事ができるようです。 デモ drag_drop.py import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * class combo(QComboBox): def __init__(self, title, parent): super(combo, self).__init__( parent) self.setAcceptDrops(True) def dragEnterEvent(self, e): print (e) if e.mimeData().hasText(): e.accept() else: e.ignore() def dropEvent(self, e): self.addItem(e.mimeData().text()) class Example(QWidget): def __init__(self): super(Example, self).__init__() self.initUI() def initUI(self): lo = QFormLayout() lo.addRow(QLabel("Type some text in textbox and drag it into combo box")) edit = QLineEdit() edit.setDragEnabled(True) com = combo("Button", self) lo.addRow(edit,com) self.setLayout(lo) self.setWindowTitle('Simple drag and drop') def main(): app = QApplication(sys.argv) ex = Example() ex.show() app.exec_() if __name__ == '__main__': main() 実行すると以下のようなwindowが表示されます。 文字を選択しinputボックスにドラッグ&ドロップすると文字が表示されました。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PyQt5のチュートリアルを動かす ➉ ~ドラッグ&ドロップ機能

はじめに PyQt5でのドラッグ&ドロップです ドラッグ&ドロップ機能はユーザーにとって直感的で使いやすいため、多くのデスクトップアプリの機能に用意されています。 QDragクラスを使用するとそのような事ができるようです。 デモ drag_drop.py import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * class combo(QComboBox): def __init__(self, title, parent): super(combo, self).__init__( parent) self.setAcceptDrops(True) def dragEnterEvent(self, e): print (e) if e.mimeData().hasText(): e.accept() else: e.ignore() def dropEvent(self, e): self.addItem(e.mimeData().text()) class Example(QWidget): def __init__(self): super(Example, self).__init__() self.initUI() def initUI(self): lo = QFormLayout() lo.addRow(QLabel("Type some text in textbox and drag it into combo box")) edit = QLineEdit() edit.setDragEnabled(True) com = combo("Button", self) lo.addRow(edit,com) self.setLayout(lo) self.setWindowTitle('Simple drag and drop') def main(): app = QApplication(sys.argv) ex = Example() ex.show() app.exec_() if __name__ == '__main__': main() 実行すると以下のようなwindowが表示されます。 文字を選択しinputボックスにドラッグ&ドロップすると文字が表示されました。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Twitterの繋がりから作成する自己紹介

こんにちは,SNS関連のデータを触るのが好きなShimasanです. この記事はAdvent Calendar 2021: ソーシャルメディアのデータを使用した分析・活用方法の12月3日分の記事です. 普段は自身のブログやnoteなどに投稿することが多く,Qiita初投稿ですがよろしくお願いいたします. どういう記事? こんにちは,SNS関連のデータを触るのが好きなShimasanです. のように自己紹介をする機会というのはエンジニア関連のイベント,学会など様々な場面で存在します. 当たり前ではありますが,自己紹介を考えるのは自分です. 自分がどのような職や経歴,立ち位置を持っているのか,趣味はなんなのかを語るのは自分自身です. この記事はそんな自己紹介になる情報を社会学の観点からTwitterのフォローユーザ,フォロワーユーザの関係情報のみで作成しよう!という試みです. この記事のゴール この記事ではTwitterのフォローユーザ,フォロワーユーザの関係情報を活用し, 興味ある領域,趣味 経歴,立ち位置 望まれている情報 を主にWordCloudを作成して表現していきます. ↑これは私の興味ある領域,趣味を表しています. 最後に本手法の制限と発展可能性について言及します. 前提共有事項 そもそも社会学の観点に基づくとは 社会学ではユーザからなるネットワークがあるときにそのユーザがどのような特性を持っているかは そのユーザ自身の情報は必要ではなく,接続されているユーザから決定されると考えるそうです. この考えかたを私はTwitterというソーシャルネットワークに適用していきます. 自分以外に適用したことあるの? あります. 自身のブログやnoteにてVTuberであったり, 少し異なりますが社会学の観点に基づいた分析としてAPEXによる文化的背景の違うユーザの交流に関する分析があります. Twitterで自己紹介とはいかなくても所属を明らかにする手法くらいあるんじゃないの? あります. 機械的に科学者の識別及び分析をしている研究なのですが,特定にTwitterのリスト機能を活用しています. 対象ユーザが含まれているリストを集めてきて,そのリストの名前からそのユーザが何かを識別するのですがこれはある程度著名なユーザにしか効果がありません. 私のような無名ユーザとは無縁の手法となっています. また,ツイートデータを使えばいいじゃないかという考えもあるかと思いますが,用いたとしても各ユーザ単位でコンテンツの揺れがでかすぎるのであまり使用したくありません. なので今回はプロフィールデータを使用しています. 手法 以下の手順で進めていきます. Twitter APIを使用して自身のフォロー,フォロワー情報を収集 その際にそれらのユーザのプロフィール部分を収集 特定のフォロー,フォロワーの組み合わせを作成(この記事のポイント) 所属するユーザのプロフィール部分の情報からWordCloudを作成 環境 Ubuntu 20.04(WSL2) Python 3.8.6 sudachipy 0.6.0 20210802のfull_dict wordcloud 1.8.1 Twitter APIを使用して自身のフォロー,フォロワー情報を収集 フォロー,フォロワーユーザそれぞれの情報を以下のTwitter APIで取得していきます. フォローユーザ ユーザid: https://api.twitter.com/1.1/friends/ids.json プロフィール: https://api.twitter.com/1.1/users/show.json descriptionフィールドを参照 フォロワーユーザ ユーザid: https://api.twitter.com/1.1/followers/ids.json プロフィール: フォローユーザと同様 特定のフォロー,フォロワーの組み合わせを作成 フォローユーザ,そしてフォロワーユーザを集合を見なした場合,その組み合わせは6通り作成されます. フォロー集合 フォロワー集合 フォロー集合とフォロワー集合の和集合 フォロー集合とフォロワー集合の積集合 (フォロー集合 – フォロワー集合)の差集合 (フォロワー集合 – フォロー集合)の差集合 特に後半の2つが今回の対象です. 簡単に言うなら,一方的にフォローしているユーザ群と一方的にフォローされているユーザ群です. 上記で紹介したブログで分析したのですが,一方的にフォローしているユーザ群は自身の願望や興味,趣味が強く表出し, 一方的にフォローされているユーザ群は自身の属性を表出させてくれます. これは直感に合う結果なのではないでしょうか. WordCloudの作成 作成したそれぞれの集合でWordCloudを作成していきます. 集合のユーザのプロフィール部分を抽出し,前処理をかけて「名詞」のみを取り出します. ストップワードはいくつか設定しましたが,ここは実行する人依存だと思います. 前処理は簡潔ではありますがneologdnをかけ,URLを除去しました. 名詞のみの取り出しはsudachipyで形態素解析して行いました.語彙はfull_dictです. 取り出したらwordcloudを作成し,終了です. 結果 一方的にフォローしているユーザ群 これは興味ある領域,趣味が一目瞭然ですね. NLPやら機械学習系,データ分析,エンジニア関連に強い興味を持ち,ゲームだったりニュースを嗜む人間のようです. めっちゃ研究に興味ある人間に見えます. これは手軽に最新の論文情報を得ることができる最高の手段がTwitterなのが悪いんです. 一方的にフォローされているユーザ群 元高専生で編入した学生という属性が出てます.フォロワーデータのみで. Twitterでネットワークを広げる際にその時期,その時期に自身に関連するユーザとつながっていくことから自然な結果に見えます. これはフォローしてくれているユーザの属性とも捉えられるので配信者やインフルエンサーに本手法を適用することでファン層を特定することができると考えられます. 制限と手法の発展について この手法はTwitterアカウントが非公開アカウントでは難しいです. また,Twitterアカウントというのは自身のパーソナリティを分割し,複数のアカウントでそれらを表現できるため,自身の一部の側面が強く表現されてしまうこともあります. パーソナリティを多くのアカウントで分割していないユーザであれば,LDAを適用すると自分の興味領域はなにかというのをそのトピック数で表現することができます. 可視化にはPyLDAvizなどを使えばお手軽に確認することができます. 私の場合はトピックを複数設定しても研究やら機械学習やらから逃げられず,マルチトピックを観測することはできませんでした.私のWordCloudを見ればマルチトピックからは程遠いのが一目瞭然なので仕方ないですね. さいごに 今回はTwitterの繋がりの情報のみで自己紹介できるのかWordCloudなどを用いて実行しました. 対象とするユーザ群を適切に設定することである程度使える情報になっていたと思います. Twitter APIを叩ける方は手法も簡単なのでぜひ試してみてください. 自分から見た自己紹介,他者との繋がりだけで見た自己紹介というネタを作れるのでいいかなと思います(私は使っていきたいと思います).
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerで、検証用サーバーを構築してみた④ (pythoneファイルの作成編)

[概要] ③docker-compose.ymlファイルの作成編の後続記事となります。  docker-compose.ymlファイルの作成編 [作業] [現在のディレクトリ構成] LocalServer/ ├── app/ └── Dockerfile └── docker-compose.yml pythonファイルの作成目的 そもそもの目的は、Dockerを使用して検証用サーバーを構築すること。 だけでなく、その検証用サーバーに対して、リクエストからレスポンスまでを一貫して行えることが 目的です。 なので、今回はpythonを使用して、検証用サーバーでの処理を記述します。 ※pythonは使ってみたかったからです。はいw pythonファイルの作成 1, ファイル名「local_server.py」を以下の内容で作成する。 local_server.py from flask import Flask app = Flask(__name__) ############################################################################# ### ローカルサーバー ############################################################################# ### ### テスト用に文字を返却するだけのメソッド ### @app.route('/') def get_text(): return 'リクエスト成功!!' <記述内容の詳細> 1行目 from flask import Flask # Flaskのimport文 2行目 app = Flask(__name__) # Flaskのインスタンス生成 11~13行目 @app.route('/test') def get_text(): return 'リクエスト成功!!' # @app.route('/'): URLとメソッドを紐付ける役割(例:http://ipアドレス/test) # def get_text(): メソッド定義 # return 'リクエスト成功!!': 文字列の返却 2, 上記ファイルを作成したら、[LocalServer/app]フォルダ直下に保存しよう。 LocalServer / app / local_server.py [参考] ・ https://docs.docker.jp/compose/overview.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerで、検証用サーバーを構築してみた④ (pythoneファイル作成編)

[概要] ③docker-compose.ymlファイルの作成編の後続記事となります。  docker-compose.ymlファイルの作成編 [作業] [現在のディレクトリ構成] LocalServer/ ├── app/ └── Dockerfile └── docker-compose.yml pythonファイルの作成目的 そもそもの目的は、Dockerを使用して検証用サーバーを構築すること。 だけでなく、その検証用サーバーに対して、リクエストからレスポンスまでを一貫して行えることが 目的です。 なので、今回はpythonを使用して、検証用サーバーでの処理を記述します。 ※pythonは使ってみたかったからです。はいw pythonファイルの作成 1, ファイル名「local_server.py」を以下の内容で作成する。 local_server.py from flask import Flask app = Flask(__name__) ############################################################################# ### ローカルサーバー ############################################################################# ### ### テスト用に文字を返却するだけのメソッド ### @app.route('/') def get_text(): return 'リクエスト成功!!' <記述内容の詳細> 1行目 from flask import Flask # Flaskのimport文 2行目 app = Flask(__name__) # Flaskのインスタンス生成 11~13行目 @app.route('/test') def get_text(): return 'リクエスト成功!!' # @app.route('/'): URLとメソッドを紐付ける役割(例:http://ipアドレス/test) # def get_text(): メソッド定義 # return 'リクエスト成功!!': 文字列の返却 2, 上記ファイルを作成したら、[LocalServer/app]フォルダ直下に保存しよう。 LocalServer / app / local_server.py
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なんでもガバにする

ガバとは  特徴の一つに曲のBPMの速さが挙げられる。140以上の高速BPMの曲も多く、なかには300近いBPMの曲も存在する。全てにおいて非常に破壊的な特徴を持つ。そのひとつとして、キックドラムの音にディストーションをかけた「ガバキック」を使用することが挙げられる。 ガバ (音楽) 出典: フリー百科事典『ウィキペディア(Wikipedia)』 ガバにする とりあえず早くしてガバキックを乗せる import numpy as np import librosa import pydub from pydub import AudioSegment path = "motoneta.mp3" kick = "gabba-kick.wav" # ガバキック音源 # とりあえずまず元ネタ音源のBPM取る y, sr = librosa.load(path) tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr) # BPMから4分のタイムを計算する tempo_one = 60 / (tempo*4) # 拡張子はうまいこと合わせて音源読み込み base_sound = AudioSegment.from_file(path, format="mp3") kick_sound = AudioSegment.from_file(kick, format="wav") # 元ネタ曲のゲインをあげてキックに負けないようにする base_sound = base_sound.apply_gain(100) # 元ネタ曲の長さを取る time = base_sound.duration_seconds # ここがメイン処理!!!!!!!!! #キック音源を元ネタ音源の上に載せる start_time_ms = 0 for i in range(1000): base_sound = base_sound.overlay(kick_sound, start_time_ms) start_time_ms = start_time_ms + tempo_one * 1000 # 適当に終わらせる if time * 1000 < start_time_ms: break # 早くする base_sound = base_sound.speedup(playback_speed=2.0, crossfade=0) # 出力する base_sound.export("gabba.mp3", format="mp3") おしまい 聞いてみた ゴミみたいだった 今後の展開 キックと元ネタ音源のバランスをとってゲイン調整する 合間で3拍目のキック抜いたりとか二つ打ちとかに勝手にしてくれるようにする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

リモートサーバ上のVSCodeでplotlyを使う【Python】

はじめに 図を生成した後に、「一部を拡大」したり「重ねてプロットした線を編集」したい!と思うことは良くあると思います。これらの要望にplotlyは応えてくれるようです。 plotlyではhtmlファイルが生成され、ローカルではhtmlファイルを立ち上げれば図を見ることができますが、リモートサーバでは少し工夫が必要でしたので紹介します。 インストール この記事ではAnaconda環境を想定しています。各自の環境に合わせてライブラリをインストールして下さい。必要なライブラリとコマンドは以下の通りです。 plotly=5.1.0 kaleido=0.2.1 ライブラリインストール conda install plotly conda install -c conda-forge python-kaleido ここで、「kaleido」はプロットした図を様々な形式で保存するために必要なライブラリです。もし、html出力とpng画像保存だけで良い方は「kaleido」をインストールする必要はありません。 VSCode拡張機能 今回の記事で用いているplotlyから出力されるhtmlファイルはjavascriptで記述されているため、VSCodeの拡張機能である「HTML Preview」や「Live HTML Previewer」ではVSCodeのUI上にグラフを表示することができません(javascript記述に非対応)。そのため、今回はVSCodeからローカルサーバを立てることのできる「Live Server」を用いることにしました。 サンプルコードを動かす ライブラリや拡張機能が正常に動作することを確認します。以下のコードを実行してみましょう。フォルダ内に「first_figure.html」(と「sample.pdf」)が生成されているはずです。 sample.py import plotly.graph_objects as go fig = go.Figure(data=go.Bar(y=[2, 3, 1])) fig.write_html('first_figure.html', auto_open=False) # kaleidoをインストールしていない方は以下の一行を除いて下さい fig.write_image('sample.pdf') ではplotlyで生成されたhtmlファイルをWebブラウザ上で見てみたいと思います。VSCodeの右下にある「Go Live」を押すと、ローカルPCのブラウザが立ち上がります。 下のようなページが立ち上がれば正常です。 このページ内にある「first_figure.html」をクリックすることにより、HTMLページを立ち上げて図を操作することができます。 終了時はVSCode右下の「Port:(ポート番号)」を押すことにより、接続を終了することができます。 最後に 前のセクションで立ち上げたHTMLページの右上にある「Download plot as png」をpdfや他の形式にできないかな〜と思っています。簡単な方法があれば教えてください。 この記事は進展があれば、更新します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【python】画像検索、翻訳、画像認識を一度にやってみる【初級篇】

この記事は、マイナビ Advent Calendar 2021 5日目の記事です。 何を作ったのか 簡単に言うと、検索ワードを入力したら自動でネットから画像を複数枚取得してきて、 それらを画像認識、物体認識をして何が映っているのかを予測するというものです。 なぜ 画像認識について素人である筆者が画像認識の勉強をするのと同時に 潤沢に存在するpythonのライブラリを多用してどんなことができるのか知りたかったからです。 イメージ 大まかな流れ ①ユーザが何かの単語を入力する(検索ワード) ②検索ワードを英語に翻訳して、フォルダを作成する ③検索ワードで画像検索をし、複数枚の画像を取得 ④取得した画像を保存する ⑤保存した画像を一つずつ読み込み、映っている物体を予測する ⑥予測結果の英単語を日本語へ翻訳する といった感じになっております。 開発環境 Google Colaboratory python 画像検索、翻訳、画像認識それぞれについて 簡単に今回使用する技術の紹介をさせていただきます。 今回使用しているライブラリはとても有名なものばかりですので、 詳しい説明のほうは省略させていただこうと思います。 画像検索 今回pythonで画像検索するためにicrawlerというライブラリを使用し、検索エンジンとしてGoogleを採用しました。 searchimage.py !pip install icrawler from icrawler.builtin import GoogleImageCrawler crawler = GoogleImageCrawler(storage={'root_dir':'cats'}) crawler.crawl(keyword='猫', max_num=100) これで猫の画像を最大100枚取得してくることができます。 keywordの引数に検索したいキーワードを入力します。 また今回はcatsフォルダに保存するために、storageで保存先の指定を行なっています。 翻訳 次に日本語から英語、英語から日本語に翻訳する必要があるため、 翻訳をしてくれるTranslatorというライブラリを使用します。 translator.py !pip install googletrans==4.0.0-rc1 from googletrans import Translator translator = Translator() text = "Hi,I'm happy" translator.translate(text=text,src='en', dest='ja').text # こんにちは、私は幸せです ほんの数行で翻訳してくれました。(恐るべしgoogletrans) パラメータについて簡単に説明すると、 srcで翻訳前の言語を指定して、destで翻訳後の言語を指定します。 translator.translateはオブジェクトを返すだけですので、.textを付けて翻訳後の文字列を取得します。 画像認識 画像認識については少し長くなってしまうため、簡単に実装部分だけ確認しておきます。 詳しいことは他のサイトを参考にしていただけますと、幸いです。 今回の開発環境はtensorflowとkerasが標準で搭載されているため、それらを使っていきたいと思います。 使用したライブラリは以下になります。 import from keras.applications.vgg16 import VGG16, decode_predictions,preprocess_input from keras.preprocessing import image from PIL import Image import numpy as np import urllib.request as urllib また今回はVGG16を使用しており、予測する部分については関数化しております。 imagedetection.py def predict(filename, size=5): img = image.load_img(filename, target_size=(224, 224)) # read image x = image.img_to_array(img) # Convert image files to numbers x = np.expand_dims(x, axis=0) # Increase dimensions pred = model.predict(preprocess_input(x)) results = decode_predictions(pred, top=size)[0] # Convert to string return results # VGG16を使用 model = VGG16(weights="imagenet") # filename:画像へのパス results = predict(filename, 10) for result in results: print(result) # 10個の予測を信頼度が高い順に表示 今回は結果の中で信頼度が最も高いものを表示するようにします。 実装する ここまでの長い長い旅も終わり、実装フェーズに入ろうと思います。 お気づきのとおり、コード自体はそこまで長いものではないので実装は簡単にできてしまいます。(すごい) install !pip install icrawler !pip install googletrans==4.0.0-rc1 import import os from keras.applications.vgg16 import VGG16, decode_predictions,preprocess_input from keras.preprocessing import image from keras.preprocessing.image import ImageDataGenerator from keras.models import Sequential, Model from keras.layers import Input, Activation, Dropout, Flatten, Dense from keras import optimizers from icrawler.builtin import GoogleImageCrawler from googletrans import Translator import numpy as np import time import shutil import glob from PIL import Image import urllib.request as urllib main.py def predict(filename, size=5): img = image.load_img(filename, target_size=(224, 224)) # read image x = image.img_to_array(img) # Convert image files to numbers x = np.expand_dims(x, axis=0) # Increase dimensions pred = model.predict(preprocess_input(x)) results = decode_predictions(pred, top=size)[0] # Convert to string return results # VGG16を使用 model = VGG16(weights="imagenet") # 検索して、フォルダを作成して、保存する keyword = input('検索:') translator = Translator() dir_name =translator.translate(text=keyword, src='ja', dest='en').text crawler = GoogleImageCrawler(storage={'root_dir':dir_name}) crawler.crawl(keyword=keyword, max_num=1000, overwrite=True) # 保存された画像から予測する path = '/content/' + translator.translate(text=keyword, src='ja', dest='en').text + '/*' files = glob.glob(path) for file in files: results = predict(file, 1) if type(results[0][1]) is not None: translated = translator.translate(results[0][1], dest='ja', src='en') print(f'{results[0][1]}({translated.text})') # 第一候補 print(str(round(results[0][2]*100))+'%') else: print(results[0][1]) # 第一候補 print(str(round(results[0][2]*100))+'%') 最後のほうで条件分岐をしているのは予測結果が返ってこなかった場合に翻訳することができないとエラーが発生してしまうので、 その場合の対処として分岐をさせています。 テスト それでは実装も終わったので、犬を例にテストを行なってみたいと思います。 実行すると検索欄が表示されるので、そこに「犬」と入力します。 そうすると「犬」の画像検索結果から画像を取得してくれており、 フォルダ「dog」が自動生成されて保存してくれました。 しばらくしてダウンロードが終了すると、保存した画像を読み込んで予測していきます。 本コードでは予測した英単語、日本語訳、信頼度の3つを表示しております。 私の保存した画像では以下のような予測結果になりました。 まとめ 今回、pythonの潤沢なライブラリを使用することで一見難しそうなシステムを初心者の私でも簡単に実装することができました。 また今回は実装できませんでしたが、応用すれば取得した画像から再学習することで様々な画像認識のできるモデルを構築できるのかなとも感じました。 機会があれば次回そこらへんの実装もできればと思います!(たぶん) それではここまで読んでいただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者がローカルサーバーを構築してみた③ (docker-compose.ymlファイルの作成編)

[概要] ②Dockerファイルの作成編の後続記事となります。  Dockerファイルの作成編 当記事は、docker-compose.ymlファイルを作成する記事となり、 不要と思われる方は飛ばしてもらえればと思います。 [作業] [ディレクトリ構成] LocalServer/ ├── app/ └── Dockerfile docker-composeのインストール確認 Dockerをインストールされている方は、dokcer-composeもインストールされている筈ですが、 確認方法としては以下のコマンドで、バージョンが表示されれば、とりあえずOKです。 terminal $ docker-compose -v docker-compose.ymlファイルとは Dockerファイルから成る、複数のコンテナを定義し実行する Docker アプリケーションのためのツールです。 簡単に、申し上げるとdokcer-compose.ymlファイルを実行すれば、1個1個のコンテナを起動させる手間がなくなるイメージ。 ※今回は、起動するコンテナは1つですが、学びのため使用 docker-compose.ymlファイルの作成 1, テキストファイルで、ファイル名「docker-compose.yml」を以下の内容で作成する。 docker-compose.yml version: '3.3' services:   flask: build: . ports: - “8080:8080" volumes: - ./app:/app environment: TZ: Asia/Toyko FLASK_APP: local_server.py FLASK_ENV: development restart: ‘no’ command: flask run —host 0.0.0.0 —port 8080 <命令文> services:お決まりの記述で、servicesの内容をネストして以下を記述しよう  flask: 今回使用するサービス(pythonのフレームワーク)でネストして以下を記述しよう   build:Dockerファイルのあるディレクトリを指す      ※ピリオドなので、カレントディレクトリを指している -> docker-compose.ymlファイルと同じ階層   ports:Dockerコンテナを立ち上げる際のポート番号   volumes:マウントする設定ファイルのパスを指定        ※マウント:使える状態にさせること      environment:環境変数設定(パスワードなど)          TZ: Asia/Toyko          ※タイムゾーンの指定                   FLASK_APP: local_server.py          ※インスタンスの場所を指定(pythonファイルを指定)          FLASK_ENV: development          ※flaskをデバッグモードで起動(ソースコードを変更したとき再読み込みする機能)           restart:自動起動設定       ※noにしてあげることで、勝手に起動することを防ぐ。   command:コマンド 2, 上記ファイルを作成したら、[LocalServer]フォルダ直下に保存しよう。 LocalServer / docker-compose.yml [参考] ・ https://docs.docker.jp/compose/overview.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerで、検証用サーバーを構築してみた③ (docker-compose.ymlファイルの作成編)

[概要] ②Dockerファイルの作成編の後続記事となります。  Dockerファイルの作成編 当記事は、docker-compose.ymlファイルを作成する記事となり、 不要と思われる方は飛ばしてもらえればと思います。 [作業] [現在のディレクトリ構成] LocalServer/ ├── app/ └── Dockerfile docker-composeのインストール確認 Dockerをインストールされている方は、dokcer-composeもインストールされている筈ですが、 確認方法としては以下のコマンドで、バージョンが表示されれば、とりあえずOKです。 terminal $ docker-compose -v docker-compose.ymlファイルとは Dockerファイルから成る、複数のコンテナを定義し実行する Docker アプリケーションのためのツールです。 簡単に、申し上げるとdokcer-compose.ymlファイルを実行すれば、1個1個のコンテナを起動させる手間がなくなるイメージ。 ※今回は、起動するコンテナは1つですが、学びのため使用 docker-compose.ymlファイルの作成 1, テキストファイルで、ファイル名「docker-compose.yml」を以下の内容で作成する。 docker-compose.yml version: '3.3' services: flask: build: . ports: - 8080:8080 volumes: - ./app:/app environment: TZ: Asia/Tokyo FLASK_APP: local_server.py FLASK_ENV: development restart: 'no' command: flask run --host 0.0.0.0 --port 8080 <命令文> services:お決まりの記述で、servicesの内容をネストして以下を記述しよう  flask: 今回使用するサービス(pythonのフレームワーク)でネストして以下を記述しよう   build:Dockerファイルのあるディレクトリを指す      ※ピリオドなので、カレントディレクトリを指している -> docker-compose.ymlファイルと同じ階層   ports:Dockerコンテナを立ち上げる際のポート番号   volumes:マウントする設定ファイルのパスを指定        ※マウント:使える状態にさせること      environment:環境変数設定(パスワードなど)          TZ: Asia/Toyko          ※タイムゾーンの指定                   FLASK_APP: local_server.py          ※インスタンスの場所を指定(pythonファイルを指定)          FLASK_ENV: development          ※flaskをデバッグモードで起動(ソースコードを変更したとき再読み込みする機能)           restart:自動起動設定       ※noにしてあげることで、勝手に起動することを防ぐ。   command:コマンド 2, 上記ファイルを作成したら、[LocalServer]フォルダ直下に保存しよう。 LocalServer / docker-compose.yml [参考] ・ https://docs.docker.jp/compose/overview.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerで、検証用サーバーを構築してみた③ (docker-compose.ymlファイル作成編)

[概要] ②Dockerファイルの作成編の後続記事となります。  Dockerファイルの作成編 当記事は、docker-compose.ymlファイルを作成する記事となり、 不要と思われる方は飛ばしてもらえればと思います。 [作業] [現在のディレクトリ構成] LocalServer/ ├── app/ └── Dockerfile docker-composeのインストール確認 Dockerをインストールされている方は、dokcer-composeもインストールされている筈ですが、 確認方法としては以下のコマンドで、バージョンが表示されれば、とりあえずOKです。 terminal $ docker-compose -v docker-compose.ymlファイルとは Dockerファイルから成る、複数のコンテナを定義し実行する Docker アプリケーションのためのツールです。 簡単に、申し上げるとdokcer-compose.ymlファイルを実行すれば、1個1個のコンテナを起動させる手間がなくなるイメージ。 ※今回は、起動するコンテナは1つですが、学びのため使用 docker-compose.ymlファイルの作成 1, テキストファイルで、ファイル名「docker-compose.yml」を以下の内容で作成する。 docker-compose.yml version: '3.3' services: flask: build: . ports: - 8080:8080 volumes: - ./app:/app environment: TZ: Asia/Tokyo FLASK_APP: local_server.py FLASK_ENV: development restart: 'no' command: flask run --host 0.0.0.0 --port 8080 <命令文> services:お決まりの記述で、servicesの内容をネストして以下を記述しよう  flask: 今回使用するサービス(pythonのフレームワーク)でネストして以下を記述しよう   build:Dockerファイルのあるディレクトリを指す      ※ピリオドなので、カレントディレクトリを指している -> docker-compose.ymlファイルと同じ階層   ports:Dockerコンテナを立ち上げる際のポート番号   volumes:マウントする設定ファイルのパスを指定        ※マウント:使える状態にさせること      environment:環境変数設定(パスワードなど)          TZ: Asia/Toyko          ※タイムゾーンの指定                   FLASK_APP: local_server.py          ※インスタンスの場所を指定(pythonファイルを指定)          FLASK_ENV: development          ※flaskをデバッグモードで起動(ソースコードを変更したとき再読み込みする機能)           restart:自動起動設定       ※noにしてあげることで、勝手に起動することを防ぐ。   command:コマンド 2, 上記ファイルを作成したら、[LocalServer]フォルダ直下に保存しよう。 LocalServer / docker-compose.yml [参考] ・ https://docs.docker.jp/compose/overview.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CDKを使ってGitHub Actions OpenID Connect (OIDC) で多段 AssumeRoleする

この記事は ハンズラボ AdventCalendar2021 3日目の記事です。 はじめに 今回は、こちらの記事のCDK化(Python)を行っていきます。 構成やcfnテンプレートなど詳細はこちらをご参照ください。 前提条件 GitHub Actions OpenID Connect (OIDC) で多段 AssumeRole するを読み構成を理解していること CDKがインストールされ実行環境が準備されていること 実装 ではさっそくやっていきます。 初期設定 ディレクトリを作成し、cdk initを実行してプロジェクトを立ち上げます。 $ mkdir github_oicd $ cd github_oicd $ cdk init app --language python これで準備完了ですので、早速コーディングしていきます。 Provider作成 まずはProviderを作成していきます。 ここはドキュメント通りに値をセットするだけですので、簡単に実装できました。 ドキュメントはこちら github_oicd_stack.py github_oidc_provider = iam.OpenIdConnectProvider(self, client_ids=["sts.amazonaws.com"], thumbprints=["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"], id="GithubOicdProvider", url="https://token.actions.githubusercontent.com", ) bastion_role(踏み台ロール)作成 ここから少し難易度が上がった印象です。 Principalに先ほど作成したProviderを設定する箇所でかなり苦戦しましたが、最後はドキュメントが助けてくれました。 (最初からドキュメントちゃんと読めという話ですが、、) 助けてくれたドキュメント達 ( Role, FederatedPrincipal, PolicyDocument, PolicyStatement ) github_oicd_stack.py GitHubOrgName = "example" GitHubRepoName = "example" conditions = { "StringEquals" : { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }, "StringLike" : { "token.actions.githubusercontent.com:sub" : f"repo:{GitHubOrgName}/{GitHubRepoName}:*" } } bastion_role = iam.Role(self, assumed_by=iam.FederatedPrincipal( github_oidc_provider.open_id_connect_provider_arn, conditions=conditions, assume_role_action="sts:AssumeRoleWithWebIdentity" ), path="/", inline_policies={ "AssumeRolePolicy": iam.PolicyDocument( statements=[ iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=["sts:GetCallerIdentity"], resources=['*'] ), iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=[ "sts:AssumeRole", "sts:TagSession" ], resources=["*"] ) ] ) }, id="BastionRole", role_name="BastionRole", ) deploy_role(デプロイ用ロール)作成 最後のロール作成です。 ここが一番難しかったです。 一度に複数のStatementを設定したかったのですが、想定している設定がなかなかできなかったので、最初に1つ設定して後付けしています。 新たに助けてくれたドキュメント達 ( ArnPrincipal, PrincipalWithConditions ) github_oicd_stack.py ExternalId = "example1234" aws_principal = iam.ArnPrincipal( arn=bastion_role.role_arn, ) # add condition aws_principal_with_condition = iam.PrincipalWithConditions(aws_principal, { "StringEquals": { "sts:ExternalId": ExternalId } }) github_actions_deploy_role = iam.Role(self, assumed_by=aws_principal_with_condition, id="GitHubActionsDeployRole", path="/", role_name="GitHubActionsDeployRole", ) # add policy github_actions_deploy_role.assume_role_policy.add_statements(iam.PolicyStatement( actions=["sts:TagSession"], principals=[ aws_principal.grant_principal, ], )) この様に書くと以下の様なポリシーになります。 完璧です。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{AccoutID}:role/BastionRole" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "example1234" } } }, { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::{AccoutID}:role/BastionRole" }, "Action": "sts:TagSession" } ] } 完成系 github_oicd_stack.py from aws_cdk import ( aws_iam as iam, core, ) class AwsCdkOicdStack(core.Stack): def __init__(self, scope: core.Construct, construct_id: str, **kwargs) -> None: super().__init__(scope, construct_id, **kwargs) ExternalId = "example1234" GitHubOrgName = "example" GitHubRepoName = "example" github_oidc_provider = iam.OpenIdConnectProvider(self, client_ids=["sts.amazonaws.com"], thumbprints=["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"], id="GithubOicdProvider", url="https://token.actions.githubusercontent.com", ) conditions = { "StringEquals" : { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }, "StringLike" : { "token.actions.githubusercontent.com:sub" : f"repo:{GitHubOrgName}/{GitHubRepoName}:*" } } bastion_role = iam.Role(self, assumed_by=iam.FederatedPrincipal( github_oidc_provider.open_id_connect_provider_arn, conditions=conditions, assume_role_action="sts:AssumeRoleWithWebIdentity" ), path="/", inline_policies={ "AssumeRolePolicy": iam.PolicyDocument( statements=[ iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=["sts:GetCallerIdentity"], resources=['*'] ), iam.PolicyStatement( effect=iam.Effect.ALLOW, actions=[ "sts:AssumeRole", "sts:TagSession" ], resources=["*"] ) ] ) }, id="BastionRole", role_name="BastionRole", ) aws_principal = iam.ArnPrincipal( arn=bastion_role.role_arn, ) # add condition aws_principal_with_condition = iam.PrincipalWithConditions(aws_principal, { "StringEquals": { "sts:ExternalId": ExternalId } }) github_actions_deploy_role = iam.Role(self, assumed_by=aws_principal_with_condition, id="GitHubActionsDeployRole", path="/", role_name="GitHubActionsDeployRole", ) # add policy github_actions_deploy_role.assume_role_policy.add_statements(iam.PolicyStatement( actions=["sts:TagSession"], principals=[ aws_principal.grant_principal, ], )) デプロイ & 動作確認 では、実際にデプロイして動作確認してみます。 Github Actions Workflowはこちらを参照してください。 $ cdk deploy 無事、それぞれのroleでAWSアカウント情報を取得することができました。 所感 個人的にはcfnよりもコーディングが楽しかったです。 今回初めてCDKを使って構築してみましたが、普段使用している言語だったためか、かなりスムーズに構築できたと思います。 cfnを初めて触った時はひどいもんでした。 みなさんも気分転換に一度CDKで構築してみてください。楽しいはずです! 以上!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】API Gateway + lambda にファイルを post、 s3 に保存

1. 概要 ローカルにある csv を lambda に post して、s3にアップロードするまでのメモ。Content-Type の設定をAPI Gateway で行うことを知らず、若干詰まったので記事にしました。 2. 目次 1. 概要 2. 目次 3. aws リソースの用意 3.1. ロールの用意 3.2. s3の作成 3.3. lambda関数の作成 3.4. api gateway の作成 3.5. 疎通の確認 4. 参考 3. aws リソースの用意 3.1. ロールの用意 次のポリシーを持ったロールを作成する AWSLambdaBasicExecutionRole AmazonS3FullAccess 3.2. s3の作成 省略 3.3. lambda関数の作成 3.1 で作成したロールを実行ロールとして指定 lambdaにデプロイするコードの作成 send_csv2s3.py import io import cgi import json import boto3 import base64 BUCKET_NAME='bucket_name' DIRECTORY='test_dir/' s3 = boto3.resource('s3') def lambda_handler(event, context): body = base64.b64decode(event['body-json']) fp = io.BytesIO(body) environ = {'REQUEST_METHOD': 'POST'} headers = { 'content-type': event['params']['header']['Content-Type'], 'content-length': len(body) } bucket = s3.Bucket(BUCKET_NAME) fs = cgi.FieldStorage(fp=fp, environ=environ, headers=headers) for f in fs.list: print("filename=" + f.filename) bucket.put_object(Body=f.value, Key=DIRECTORY+f.filename) return { 'statusCode': 200, 'body': json.dumps('Hello World!!') } 3.4. api gateway の作成 REST API の構築から作成 アクションからメソッドの作成(POST) 3.3 で作成した lambda を関数として作成 Content-Type: multipart/form-data を受け取る設定を追加 リソース>POST>マッピングテンプレート リクエスト本文のパススルーはテンプレートが定義されていない場合を選択 Content-Type に multipart/form-data を入力しチェックマークをクリック テンプレートの生成はプルダウンより メソッドリクエストのパススルー を選択 設定>バイナリメディアタイプ multipart/form-data を追加し保存 IPアドレス制限 リソースポリシーに以下を記述 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": "execute-api:Invoke", "Resource": "arn:aws:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, { "Effect": "Deny", "Principal": "*", "Action": "execute-api:Invoke", "Resource": "arn:aws:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Condition": { "NotIpAddress": { "aws:SourceIp": "xxx.xxx.xxx.xxx/yy" } } } ] } デプロイ リソース>アクション>APIのデプロイ 3.5. 疎通の確認 csv をポストするコード作成、実行 send_csv.py import os import sys import json import requests from glob import glob from pathlib import Path def main(): csv_dir = "./xxx/xxx" csvs = glob(csv_dir + "*.csv") a_csv_path = csvs[0] url = "https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/yyyy" # api gateway>ステージ>urlの呼び出しから確認する file_name = os.path.basename(a_csv_path) file = {'file': (file_name, open(a_csv_path, 'rb'), 'text/csv')} res = requests.post(url, files=file) print(res) print(res.text) if __name__ == "__main__": main() 結果の確認 完了!!(エラーハンドリング等については追記してください m・・m) 4. 参考 API Gateway の IP制限 API Gateway で multipart/form-data を受け取る
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む