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

三角比を考えるときの図をpythonで書く

やったこと 三角関数について調べる機会があったので、もっと何かできないかと思い、三角比を考えるときによく出てくる図を描画する処理をpythonで書いてみました。 triangle_drow()に角度を渡してやると図を書いて、座標の情報を表示してくれます。 # coding: utf-8 import matplotlib.pyplot as plt import numpy as np #三角比を図に描画 def triangle_drow(angle): #X座標とY座標を算出 x1 = np.cos(np.radians(angle)) y1 = np.sin(np.radians(angle)) #円の上に点Pをプロットして座標を表示 plt.scatter(x1,y1,color="blue") target_text = " P(" + str(round(x1,3)) + "," + str(round(y1,3)) + ")" plt.text(x1,y1,target_text) #点Pを使って三角形を作成 plt.plot([0,x1],[0,y1],linewidth=3,color="blue") plt.plot([x1,x1],[0,y1],linewidth=3,color="blue") plt.plot([0,x1],[0,0],linewidth=3,color="blue") #メイン処理 #軸の描写 plt.axhline(0, linewidth=3, color="black") plt.axvline(0, linewidth=3, color="black") #円の描写 angle = np.arange(0,360) #0~360の角度のリストを作成 circle_x = np.cos(np.radians(angle)) #x座標のリスト circle_y = np.sin(np.radians(angle)) #y座標のリスト plt.plot(circle_x,circle_y, linestyle="dotted") #引数で渡した角度の三角形を描画 triangle_drow(45) plt.axis('equal') #x軸とy軸のスケールを揃える plt.show() 出力結果
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ラビットチャレンジ【E資格】 深層学習day4

初めに 本記事はJDLAのE資格の認定プログラム「ラビット・チャレンジ」における深層学習day4のレポート記事です。この記事では以下の内容について、そのモデルの概念から確認し、数式・実装を含めてまとめていきます。 強化学習 AlphaGo 軽量化・高速化技術 応用モデル Transformer 物体検知・セグメンテーション DCGAN 強化学習 強化学習(Reinforcement learning)とは 長期的に報酬を最大化できるように環境の中で行動を選択できるエージェントを作ることを目標とする機械学習の一分野です。行動の毛かkとして与えられる利益を元に、行動を決定する原理を改善していく仕組みです。教師あり学習や教師なし学習と違い、学習データなしに自身の試行錯誤のみで学習するのが特徴です。 エージェントと環境 強化学習では、行動する主体を「エージェント」、エージェントがいる世界を「環境」と呼びます。 行動と状態 エージェントが環境に行う働きかけを「行動」と呼びます。エージェントは様々な行動をとることができますが、どの行動を採るかによって、その後の状況も変わります。 報酬 強化学習では、行動の良さを示す指標として「報酬」を使います。 即時報酬と遅延報酬 エージェントは基本的に報酬がたくさんもらえる行動を選べばよいのですが、行動直後に発生する報酬にこだわると、後で発生するかもしれない大きな報酬を見逃してしまします。行動直後に発生する報酬を「即時報酬」、後で遅れて発生する手法を「遅延報酬」と呼びます。 収益と価値 収益 即時報酬だけでなく、後に発生する全ての遅延報酬を含めた報酬の合計を最大化することです。報酬が環境から与えられるものなのに対して、収益は最大化したい目標としてエージェント自身が設定するものです。 価値 エージェントの状態と方策を固定した場合の条件付き収益を計算します。 この価値が大きくなる条件を探し出せれば、学習ができていることになります。つまり、「価値の最大化」が「収益の最大化」につながり、さらには「多くの報酬をもらえる方策」という強化学習の目的につながります。 強化学習の応用例 マーケティングの場合 環境(会社の販売促進部) エージェント(プロフィールと購入履歴に基づいて、キャンペーンメールを送る顧客を決めるソフトウェア) 行動(顧客ごとに送信、非送信の二つの行動を選ぶソフトウェア) 報酬(キャンペーンのコストという負の報酬とキャンペーンで生み出されると推測される売上というせいの報酬) 探索と利用のトレードオフ 環境について事前に完璧な知識があれば、最適な行動を予測して決定することは可能です。 どのような顧客にキャンペーンメールを送信するとどのような行動を行うのかが既知である状況 ↓ 強化学習の場合、上記の過程成り立たないとする。不完全な知識を元に行動しながら、データを収集して最適な行動を見つけていきます。 探索が足りない状態(過去のデータで、ベストとされる行動のみを常に取り続ければ、他にもっとベストな行動を見つけることはできない) ↑ トレードオフの関係性 ↓ 利用が足りない状態(未知の行動のみを常にとり続ければ、過去の経験が活かせない) 強化学習のイメージ 強化学習の歴史 Q学習(行動価値観数を行動する毎に更新することにより学習を進める方法) 関数近似法(価値観数や方策関数を関数近似する手法のこと) 価値関数 状態価値観数 ある状態に注目する場合 行動価値観数 状態と価値を組み合わせた価値に注目する場合 マルコフ決定過程 強化学習の学習サイクルについて、マルコフ決定過程と呼ばれる手法を例に説明します。 マルコフ決定過程は、「次の状態」が「現在の状態」と採った「行動」によって確定するシステムです。 エージェントは、最初は何をすべきか判断できないため、採れる行動の中からランダムに決定する。 エージェントは報酬をもらえた時に、どのような状態で、どのような行動をしたら、どの程度の報酬がもらえたかという経験を記録する。 経験に応じて方策を求める。 ランダムな動きはしつつ、方策を手掛かりに行動を決定する。 2~4を繰り返して、将来的(ゲーム終了時まで)んじ多くの報酬を得られる方策を求める。 方策関数 方策関数とは、方策ベースの強化学習手法において、ある状態でどのような行動を取るのかの確率を与える関数のことです。 方策勾配法 方策を求める手法は、「方策反復法」と「価値反復法」の2つに大別されます。 方策反復法 方策に従って行動し、成功時の行動は重要と考え、その行動を多く取り入れるように方策を更新する手法です。この手法を利用したアルゴリズムの一つが方策勾配法になります。方策勾配法は以下の式で表されます。 $$ \theta^{t+1}=\theta^{t}+\varepsilon \nabla J(\theta) $$ 方策関数の重みを更新する式 \nabla_{\theta} J(\theta)=\mathbb{E}_{\pi_{\theta}}\left[\left(\nabla_{\theta} \log \pi_{\theta}(a \mid s) Q^{\pi}(s, a)\right)\right] 価値反復法 次の状態価値と今の状態価値の差分を計算し、その差分だけ今の状態価値を増やすような手法です。この手法を利用したアルゴリズムがSarsaとQ学習になります。 AlphaGo AlphaGo Lee 方策と価値は関数として表すことができるので、それぞれ、方策関数、価値関数としてニューラルネットワークで学習させることができます。PolicyNetでは、盤面からの情報を19×19×48として入力します。活性化関数はSoftMaxを使用しています。 ネットワークの構造図 出力は現局面の勝率を-1~1で表したいため、全結合層を用いて単一の値を出力するようにして、活性化関数としてtanhを用いています。 PolicyNetとValueNetの入力について Alpha Goの学習 1, 教師あり学習によるRollOutPolicyとPolicyNetの学習 2, 強化学習によるPolicyNetの学習 3, 強化学習によるValueNetの学習 PolicyNetの強化学習 現状のPolicyNetとPolicyPoolからランダムに選択されたPolicyNetと対局シミュレーションを行い、その結果を用いて方策勾配法で学習を行います。PolicyPoolとは、PolicyNetの強化学習の過程を500Iteraionごとに記録し保存しておいたものです。現状のPolicyNet同士の対局ではなく、PolicyPoolに保存されているものとの対局を使用する理由は、対局に幅を持たせて過学習を防ぐのが主な目的です。この学習をminibatch size 128で1万回行いました。 ValueNetの強化学習 PolicyNetを使用して対局シミュレーションを行い、その結果の勝敗を教師として学習させます。 教師データの作成手順は 1. SL PolicyNet(教師あり学習で作成したPolicyNet)でN手まで打つ。 2. N+1手目の手をランダムに選択し、その手で進めた局面をS(N+1)とする。 3. S(N+1)からRL PolicyNet(強化学習で作成したPolicyNet)で終局まで打ち、その勝敗報酬をRとする。 S(N+1)とRを教師データ対として、回帰問題としてminibatch size 32で5000万回の学習を行います。 N手までとN+1手からのPolicyNetを別々にしてある理由は、過学習を防ぐためであると論文では説明されています。 RollOutPolicy ニューラルネットワーク(NN)ではなく、線形の方策関数です。NNを用いたPolicyNetでは学習に時間が掛かかりますが、RollOutPolicyは探索中に高速に着手確率を出すために使用されます(PolicyNetと比較して計算速度は速いものの精度は落ちます)。 モンテカルロ木探索 コンピュータ囲碁ソフトで現在最も有効とされている探索法です。 モンテカルロ木探索は、盤面の価値や勝率予想値が必要となるminmax探索やαβ探索には課題がありました。そこで盤面評価値に頼らず末端評価値、つまり勝敗のみを使って探索を行うことができないかという発想で生まれた探索法です。 探索の流れ 現局面から末端局面までPlayOutと呼ばれるランダムシミュレーションを多数回行い、その勝敗を集計して着手の優劣を決定する。 該当手のシミュレーション回数が一定数を超えたら、その手を着手したあとの局面をシミュレーション開始局面とするよう、探索木を成長させる。 この探索木の成長を行うというのがモンテカルロ木探索の優れているところです。モンテカルロ木探索はこの木の成長を行うことによって、一定条件下において探索結果は、最善手を返すということが理論的に証明されています。 AlphaGo Zero AlphaGo(Lee)とAlphaGo Zeroの違い 教師あり学習を行わず、強化学習のみで作成 特徴入力からヒューリスティックな要素を排除し、石の配置のみにした PolicyNetとValueNetを1つのネットワークに統合した Residual Netを導入 モンテカルロ木探索からRollOutシミュレーションを無くした AlphaGo ZeroのNetwork Residual Block以降、PolicyNetとValueNetが分かれる構造になっています。 Residual Blockの基本形 Residual Blockには、ネットワークにショートカット構造を追加して、勾配の爆発と消失を抑える意図があります。結果として、下記の構造を用いることで、100層を超えるネットワークでの安定した学習が可能になりました。 Residual Networkの派生系 モデルの工夫について、試験でも対応しているモデルと名前を確認します。命名されていますが、あくまで基本的な概念(畳み込みやプーリング)を抑えれば、内容の理解がしやすいですし、覚えやすいです。 Alpha Go Zeroの学習法 Alpha Goの学習は「自己対局による教師データの作成」「学習」「ネットワークの更新」の3つのステップで構成されています。 自己対局による教師データの作成 現状のネットワークでモンテカルロ木探索を用いて自己対局を行います。まず30手までランダムで打ち、そこから探索を行い勝敗を決定します。自己対局中の各局面での着手選択確率分布と勝敗を記録します。教師データの形は(局面、着手選択確率分布、勝敗)が1セットとなります。 学習 自己対局で作成した教師データを使い学習を行います。NetworkのPolicy部分の教師として着手選択確率分布を用い、Value部分の教師に勝敗を用います。損失関数はPolicy部分はCrossEntropy、Value部分は平均二乗誤差です。 ネットワークの更新 学習後、現状のネットワークと学習後のネットワークとで対局テストを行い、学習後のネットワークの勝率が高かった場合、学習後のネットワークを現状のネットワークとします。 軽量化・高速化技術 軽量化技術:非常に性能が悪いコンピュータの上でも動かせるようにする技術 分散深層学習(現在最も重要な技術) 深層学習は多くのデータを使用したり、パラメータ調整のために多くの時間を使用したりするため、高速な計算が求められます。複数の計算資源(ワーカー)を使用し、並列的にニューラルネットを構成することで、効率の良い学習を行いたい。そのために、データ並列化、モデル並列化、GPUによる高速技術は不可欠です。毎年10倍の勢いでデータの量やモデルの大きさが増えているのが大きな問題で、ムーアの法則からもわかるように、コンピュータの性能の向上よりもはるかに早く伸びている。そこで分散処理することで、現在のPCの性能でも処理できるようになっている。 データ並列 親モデルを各ワーカーに子モデルとしてコピーし、データを分割させ各ワーカーごとに計算させます。 (一台のパソコンだけ使用する場合→ワーカー1) 同期型 同期型のパラメータ更新の流れ 各ワーカーが計算し終えるのを待ち、全ワーカーの勾配が出たところで、勾配の平均を計算して、親モデルのパラメータを更新します。 非同期型 非同期型のパラメータ更新の流れ 各ワーカーはお互いの計算を待たずに各モデルごとに更新を行います。学習が終わったモデルはパラメータサーバーにpushされます。新たに学習を始める時は、パラメーターサーバーからPopしたモデルに対して学習していきます。 同期型と非同期型の比較 処理のスピードは、お互いのワーカーの計算を待たない非同期型の方が早い。 非同期型は最新のモデルのパラメータを利用できないので、学習が不安定になりやすい。 現在は同期型の方が精度が良いことが多いので、主流となっている。 モデル並列 親モデルを各ワーカーに分割し、それぞれのモデルを学習させます。全てのデータで学習が終わった後で、一つのモデルに復元します。モデルが大きい時はモデル並列化を、データが大きい時はデータ並列化をすると良いです。 モデル並列化の効果 大きなモデルほど効果が大きいです。つまり、モデルのパラメータ数が多いほど、スピードアップの効率も向上します。 GPU(Graphics Processing Unit) GPUとは3Dグラフィックスを描画する際に必要な計算処理を行う半導体チップのことです。 GPUは主に画面の描画の処理など単純な処理に適しています。 GPUによる高速化 GPGPU (General-purpose on GPU) 元々の使用目的であるグラフィック以外の用途で使用されるGPUの総称 CPU(Central Processing Unit) 高性能なコアが少数 複雑で連続的な処理が得意 CPUはパソコンにおける頭脳部分に当たり汎用的な複雑な処理を行う GPU 比較的低性能なコアが多数 簡単な並列処理が得意 ニューラルネットの学習は単純な行列演算が多いので、高速化が可能になる CPUに比べて多くのコアを搭載しているため、同時に対応できる処理が多い 軽量化の手法 量子化(Quantize:一番良く用いられる手法) ネットワークが大きくなると大量のパラメータが必要となり、学習や推論に多くのメモリと演算処理が必要になります。量子化とは重みなどのパラーメータをより小さいビットで表現することで、モデルの軽量化を図る手法です。使用するビットを制限することでネットワークの構造を変えずにメモリ使用量を削減できます。つまり、通常のパラメータの64bit浮動小数点を、32bitなど下位の精度に落とすことでメモリと演算処理の削減を行います。この時、計算の高速化、省メモリ化の一方で、精度は低下します。 蒸留(Distillation) 規模の大きなモデルの知識を用いて、軽量なモデルの作成を行う手法です。つまり、大きいモデルやアンサンブルモデルを教師モデルとして、その知識を小さいモデル(生徒モデル)の学習に利用することで、大きいモデルに匹敵する精度を持つ小さいモデルを作ることが期待できます。 一度学習した精度の高いモデルの知識(予測結果)を別の軽量なモデルに継承するということです。蒸留によって少ない学習回数により精度の良いモデルを作成できます。 枝刈り(Pruning) Deep Learningのノード間の重みが小さい箇所の接続を削除する、または影響の小さいノードを削除することでパラメータ数を削減する手法をPruning(プルーニング:枝刈り)と呼びます。ノードや重みを削除することでパラメータ数を減少させます。記憶する必要があるパラメータが減少することによって、計算する回数が削減され、メモリ使用量が少なくなります。その結果、モデルの軽量化と処理の高速化が期待できます。 計算の高速化 寄与の少ないニューロンの削減を行い、モデルの圧縮を行うことで高速化に計算を行うことができます。 ニューロンの削減 ニューロンの削減の手法は重みが閾値以下の場合、ニューロンを削減し、再学習を行います。 下記の図でもわかるように、このPruningでは想像以上に削減できるにもかかわらず、性能はあまり変わりません。 応用モデル MobileNet DeepLearningモデルの軽量化・高速化・高精度化を実現するために、MobileNetでは畳み込みの演算を工夫しています。現在は第三世代まである。 MobileNetのアーキテクチャ 以下のような6ピクセル×6ピクセルの36ピクセルのカラー画像(3チャンネル分)に対して畳み込みを行う場合、3つの情報を元に、4つを出力としてカーネルが働きます。 この1点を計算するための計算量は$K \times K \times C \times M$ 出力のデータを全部作りたい場合は$H \times W \times K \times K \times C \times M$ Depthwise ConvolutionとPointwise Convolutionの組み合わせで軽量化を実現しています。 Depthwise Convolution 仕組みとしては、入力マップのチャンネルごとに畳み込みを実施します。出力マップをそれらと結合することで、入力マップのチャネル数と同じになります。これにより、計算量が大幅に削減できます。 一般的な畳み込み:$H \times W \times K \times K \times C \times M$ Depthwise Convolution:$H \times W \times C \times K \times K$ Pointwise Convolution 1×1convとも呼ばれています。入力マップのポイントごとに畳み込みを実施します。出力マップでは、フィルタ数分だけ作成可能です。 一般的な畳み込み Pointwise Convolution:$H \times W \times C \times M$ 確認問題 MobileNetのアーキテクチャについて Depthwith Separable Convolutionという手法を用いて計算量を削減している。通常の畳み込みが空間方向とチャネル方向の」計算を同時に行うのに対して、Depthwith Separable ConvolutionではそれらをDepthwith ConvolutionとPointwise Convolutionと呼ばれる演算によって個別に行う。 Depthtwise Convolutionはチャネル毎に空間方向へ畳み込む。すなわち、チャネル毎に$D_{K} \times D_{K} \times 1$のサイズのフィルターをそれぞれ用いて計算を行うため、その計算量は$K \times K \times C \times M$までで1ピクセル分なので、Depthtwise Convolutionでは$H \times W \times C \times K \times K$となる。 次にDepthwith Convolutionの出力をPointwise Convolutionによってチャネル方向に畳み込む。すなわち、出力チャネル毎に$1 \times 1 \times M$サイズのフィルターをそれぞれ用いて計算を行うため、その計算量は$H \times W \times C \times M$となる。 原論文MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications DenseNet Dense Convolutional Networkは畳み込みニューラルネットワークアーキテクチャの一種です。ニューラルネットワークではそうが深くなるにつれて、学習が難しくなるという問題がありましたが、Residual NetworkなどのCNNアーキテクチャでは前方の層から後方の層へアイデンティティ接続を介してパスを作ることで対処した、DenseBlockと呼ばれるモジュールを用いました。DenseNetでも同様のアーキテクチャを採用しています。 DenseNetのアーキテクチャについて メイン側はショートカット側で得られたフィルターを重ねていきます。論文では「メイン側は”The global state”と呼ばれ、そのグローバルな状態に対してDenseBlockが貢献していく」とあるため、ResNetと同様に考えるなら、ショートカット側で畳み込みをしていると考えるのが自然です。 初期の畳み込み DenseBlock 変換レイヤー 判別レイヤー 以下のように「DenseBlock(s)→Transition→DenseBlock(s)→…」と交互に重ねていくのがDenseNetの全体の構造です。DenseBlockの層で×6、×12とありますがこれは1x1→3x3畳み込みからなるDenseBlockを6個、12個重ねことです。 DenseNetとResNetの違い(スキップコネクションのアーキテクチャ) DenseBlockでは前方の各層からの出力全てが後方の層へ入力として用いられています。 RessidualBlockでは前1層の入力のみ後方の層へと入力します。 DenseBlockではとgrowth rate(成長率:k)呼ばれるハイパーパラメータが存在します。 原論文Densely Connected Convolutional Networks GithubDenseNet Batch Normalization Batch Normalizationは2015年に“Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift”というで最初に提案された仕組みになります。Batch Normalizationは、Deep Learningにおける各重みパラメータを上手くreparametrizationすることで、ネットワークを最適化するための方法の一つです。 $H \times W \times C$のサンプルがN個あった場合に、N個の同一チャネルが正規化の単位 RGBの3チャネルの場合には各チャネルの平均と分散を求め、正規化を実施します。 ミニバッチのサイズを大きく取れない場合には、効果が薄くなってしまう。 原論文:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift BatchNorm以外の正規化:Layer正規化/Instance正規化 Batch Normalizationはシンプルで非常に効果的な方法ですが、以下の問題点が指摘されています。 データセット全体の平均・分散ではなく、ミニバッチごとに平均・分散を計算するため、ミニ・バッチが小さい場合、平均・分散が不安定になる 再帰的ニューラルネットワークに適用するのが難しい Layer Normalization(それぞれのサンプルの全てのピクセルが同一分布に従うように正規化) そこで、Layer Normalizationでは、1つのサンプルにおける各レイヤーの隠れ層の値の平均・分散で正規化します。 \begin{aligned} \mu^{l} &=\frac{1}{H} \sum_{i=1}^{H} a_{i}^{l} \\ \sigma^{l} &=\sqrt{\frac{1}{H} \sum_{i=1}^{H}\left(a_{i}^{l}-\mu^{l}\right)^{2}} \end{aligned} Layer正規化はN個の統計量を求めて、サンプル毎に正規化をします。層正規化はサンプル毎の統計量が大きく変わるような場合に有効であり、RNNやTransformerなどで利用されています。現在Transformerが注目されていることもあり、層正規化も広く使われる様になってきています。 Layer Normalization Instance Normalization(さらにチャネルも同一分布に従うように正規化) インスタンスの正規化は、グループのサイズがチャネルサイズ(または軸のサイズ)と同じサイズであるグループ正規化の特殊な場合です。各サブプロットは、入力テンソルを示しています。Nはバッチ軸、Cはチャネル軸、(H、W)は空間軸(画像の高さと幅など)です。青のピクセルは、これらのピクセルの値を集計することによって計算された、同じ平均と分散によって正規化されます。 原論文:Instance Normalization: The Missing Ingredient for Fast Stylization Wavenet PixelCNNをベースにした音声波形を生成するためのディープニューラルネットワークの一つです。音声波形のデータセットを$\boldsymbol{x}=\left{x_{1}, x_{2}, \cdots, x_{T}\right}$として, 以下のように条件付き確率で生成されると仮定します。つまり、音声サンプル$x_{t}$は前時点の全てにおけるサンプルに条件づけられます。 $$ p(\mathbf{x})=\prod_{t=1}^{T} p\left(x_{t} \mid x_{1}, \cdots, x_{t-1}\right) $$ WaveNetのメイン部分としては、時系列データセットに対してConvolutionを組み込んでいます。 既存の畳み込み 畳み込み(Dilated) 確認テスト 確認テスト1 深層学習を用いて結合確率を学習する際に、効率的に学習が行えるアーキテクチャを提案したことがWaveNetの大きな貢献の1つである。提案された新しいConvolution 型アーキテクチャは(あ)と呼ばれ、結合確率を効率的に学習できるようになっている。(あ)に入るものを選べ。 1) Dilated causal convolution 2) Depthwise separable convolution 3) Pointwise convolution 4) Deconvolution -> 1) Dilated causal convolution 確認テスト2 Dilated causal convolutionを用いた際の大きな利点は、単純なConvolution layer と比べて(い)ことである。 1) パラメータ数に対する受容野が広い 2) 受容野あたりのパラメータ数が多い 3) 学習時に並列計算が行える 4) 推論時に並列計算が行える -> 1) パラメータ数に対する受容野が広い WAVENET: A GENERATIVE MODEL FOR RAW AUDIO Transformer TransformerはRNNやCNNを使用せず、Attentionのみを用いるSeq2Seqモデル(翻訳タスクにおいて、Seq2seq(RNNベースEncoder-Decoderモデル)よりも早くて精度が高いという優れもの)です。並列計算が可能なためRNNに比べて計算が高速な上、Self-Attentionと呼ばれる機構を用いることにより、局所的な位置しか参照できないCNNと異なり、系列内の任意の位置の情報を参照することを可能にしています。その他にもいくつかの工夫が加えられており、翻訳に限らない自然言語処理のあらゆるタスクで圧倒的な性能を示すことが知られています。 Transformerの特徴 再帰も畳み込みも一切使わない 翻訳でSoTAを超えるBLEUスコア(28.4)を叩き出した 並列化がかなりしやすく訓練時間が圧倒的に削減できる Transformerは他のタスクにも汎用性が高い 経緯として、RNNとエンコーダ-デコーダモデルがこれまでのNLP界を牽引してきましたが、逐次的に単語を処理するがゆえに訓練時に並列処理できないという大きな欠点がありました。長文に対してはAttentionが使われていましたが、そのAttentionは概ねRNNと一緒に使われていました。そこで本論文(Attention Is All You Need(2017))では、RNNを一切使わずにAttentionだけを使うことで、入力と出力の文章同士の広範囲な依存関係を捉えられるモデルTransformerを提案しています。 モデル構造 Transformerの基本的構造はエンコーダ-デコーダモデルでself-attention層とPosition-wise全結合層を使用していることが大枠の特徴となります。 エンコーダー-デコーダモデル Transformerの全体像(左半分がエンコーダ、右半分がデコーダ) Vaswani, A. et al.(2017) Attention Is All You Need Attention機構 Attentionとは、文中のある単語の意味を理解する時に、文中の単語のどれに注目すれば良いかを表すスコアのことです。 Self-Attentionを使用する理由 計算量が小さい 並列計算可能 広範囲の依存関係を学習可能 高い解釈可能性 工夫 最適化関数(Adam) 正則化(ドロップアウト、ラベルスムージング) 所感 近年のNLP界隈で発表される最新モデル(BERT, BPT-3など)はどれもTransformerをbase modelとしており、そのTransformerの提案論文である「Attention Is All You Need(2017)」はNLPの動向を理解するには必読の論文であると思いました。 参考文献 原論文:Attention Is All You Need(2017) The Illustrated Transformer BLEUスコア 参考実装 実装コード Transformerモデル実装 物体検知・セグメンテーション 物体認識タスク 分類(Classification) 入力画像がどのラベルに属するものなのかを分類します。分類では、対象画像のどの位置に物体があるのかについては興味がなく、ラベルに分類することがタスクになります。 物体検知(Object Detection) 物体検出では、画像分類と同じく対象画像内に写っている物体が属するラベルに加え、その物体が写っている位置も明らかにする必要があります。この位置については、バウンディングボックスと呼ばれる矩形で囲うことで表現します。 意味領域分割(Semantic Segmentation) 意味領域分割では、各ピクセルに対してラベルが割り当てられています。バウンディングボックスに対象物が収まらなければならない物体検知とは対照的に、不規則な形状の対象物を明瞭に検出することができます。 個体領域分割(Instance Segmentation) 各ピクセルにラベルが与えられ、さらにインスタンスの区別も付けられるようになります。意味領域分割では全て同じとみなしてたものも、個体領域分割では、物体個々の区別に興味があるため区別できます。 代表データセット VOC12(VOC=Visual Object Classes) クラス数:20 Train+Valデータ数:11,540 Box/画像(物体数/1枚の画像):2.4 Instance Annotation(物体個々にラベリング)が付与 ILSVRC17(ImageNetのサブセット) クラス数:200 Train+Valデータ数:476,668 Box/画像(物体数/1枚の画像):1.1 MS COCO18(MS=Microsoft, COCO=Common Object in Context) クラス数:80 Train+Valデータ数:123,287 Box/画像(物体数/1枚の画像):7.3 Instance Annotation(物体個々にラベリング)が付与 OICOD18(Open Images V4のサブセット) クラス数:500 Train+Valデータ数:1,743,042 Box/画像(物体数/1枚の画像):7.0 Instance Annotation(物体個々にラベリング)が付与 評価指標 適合率(Precision)はPositive と予測したデータ(TP + FP)の中で実際にPositiveだったデータ(TP)数の割合。この値が高いほど性能が良く、間違った分類が少ないということを意味します。 再現率(Recall)は実際にPositiveであるデータ(TP + FN)の中で、正しくPositiveと予測された(TP)数の割合でした。この値が高いほど性能がよく、間違ったPositiveの判断が少ないという事を意味します。 PR曲線(Precison Recall curve)はPrecisonを縦軸、Recallを横軸に取ります。Positiveに予測されるかNegativeに予測されるかは、予測値と閾値の値によって決まります。予測値0.7という値が得られたとき、閾値が0.5だとしたら予測結果はPositiveになります。一方で閾値が0.8だった場合、予測結果はNegativeになります。この閾値を0~1と変化させたときのPrecisonとRecallの関係がPR曲線となります。 引用:ラビットチャレンジ講義資料 IoU(Intersection over Union) IoUはGround truth Bounding Boxとpredicted Bounding boxのOverlap/Unionです。物体検出においてはクラスラベルだけでなく、物体位置の予測精度も評価したいというモチベーションがあります。 $$ I o U=\frac{\text { Area of Overlap }}{\text { Area of Union }} $$ 混同行列を用いて表現 $$ I o U=\frac{T P}{T P+F P+F N} $$ Precision/Recall 物体検出ではconfidenceの他にIoUについても閾値を用意する必要があります。そこで物体検知において、入力画像1枚を見たときのPrecision/Recall指標について具体例を見ていきます。以下の図ではconfidenceの閾値を0.5としているので、まず「conf.> 0.5」となるクラスラベルの予測が表にピックアップされています。そしてIoUの閾値0.5より大きいものについては、物体位置の予測精度も良いとしてTPとなります(注意:既に検出済みの対象では「IoU > 0.5」でもFPになる)。同じ物体に対して複数のPredicted BBが出てきてしまったときは、最もconfidenceが高く、かつIoUの閾値を超えているものだけをTPとします。TPとFPの数が分かるので、PrecisionとRecallの計算が可能となります。 引用:ラビットチャレンジ講義資料 \begin{gathered} \text { Precision }=\frac{T P}{T P+F P}=\frac{3}{3+3}=0.5 \\ \text { Recall }=\frac{T P}{T P+F N}=\frac{3}{3+0}=1.0 \end{gathered} 次にクラス単位で画像が複数ある場合のPrecision/Recall指標についてみていきます。ここでは、まず「人」のクラスラベルだけに着目していきます。confidenceの閾値を0.5、IoUの閾値を0.5に設定しています。 1, 表のP1Ground-Truth1について「confidence 0.92」で人を検出・「IoU 0.88」の精度で位置を検出 2, 表のP4Ground-Truth3について「confidence 0.70」で人を検出・「IoU 0.83」の精度で位置を検出 1と2について、入力画像1枚の時と同じくTPであるかFPであるかの判断をしていき、PrecisionとRecallを求めます。Recallの計算では分母に+1があり、Recallの分母は、すべてのGround-Truth数を表します。今回、P1~P6までpicture4の「人」は一度も検出されませんでした。つまり、本当は検出する対象であるにもかかわらず検出できなかったもの(FN)が1つ存在するという事です。<- +1に該当 \begin{aligned} &\text { Precision }=\frac{T P}{T P+F P}=\frac{3}{3+3}=0.5 \\ &\text { Recall }=\frac{T P}{T P+F N}=\frac{3}{3+1}=0.75 \end{aligned} Average Precision (AP) 複数画像の場合、Precision/Recallではconfidenceの閾値を0.5で計算をしていました。このconfidenceの閾値ごとにPrecision/Recallを算出してPR曲線を作成し、PR曲線の下側面積が物体検知で用いられる指標がAverage Precisionになります。 confidenceの閾値をβとすると、βの関数としてprecisionとRecallが記述できます。 \begin{gathered} \text { Recall }=R(\beta) \\ \text { Precision }=P(\beta) \end{gathered} これらを一つにまとめ、Recallの関数としてprecisionを記述することができます。 PR曲線の式 APはPR曲線の下側面積となるので、次の式で表すことができます。 $$ P=f(R) $$ $$ A P=\int_{0}^{1} P(R) d R $$ 以上が、物体検知における評価指標であるAPの導出です。 mean Average Precision (mAP) 各クラスごとにAPを求め、算術平均したものがmAPです。 $$ m A P=\frac{1}{C} \sum_{i=1}^{C} A P_{i} $$ mAP COCO mAP COCOはMS COCOで導入された物体検知の評価指標です。これまで、mAPの導出はIoUの閾値を0.5に固定していました。mAP COCOはIoUの閾値を0.5~0.95まで0.05刻みで変化させたときの各AP、mAPを計算します。IoUの閾値毎のmAPを算術平均したものがmAP COCOです。 $$ m A P_{C O C O}=\frac{m A P_{0.5}+m A P_{0.55}+\ldots+m A P_{0.95}}{10} $$ この指標はIoUの閾値を0.5より大きいものにしてmAPを求めます。つまり、Ground-Truth BBに被ったようなPredicted BBを評価する指標になります。 Flames per Second (FPS) 物体検知では、検出精度に加えて検出速度も重要です。横軸はFrame Pre Secondであり、1秒あたりに何フレーム処理できるかという処理速度を測る指標です(大きいほど処理速度が速い)。 inference timeが用いられることもあります。つまり、1フレームあたり何秒処理に時間が掛かったかという推論の時間を表します(小さいほど処理速度が速い)。 物体検知のフレームワーク 物体検知のフレームワークは大きく分けて2種類あります。 (a)一段階 (b)二段階 歴史 2段階検出器(Two-stage detector) RCNN、SPPNet、Fast RCNN、Faster RCNN、RFCN、FPN、Mask RCNN 1, 候補領域の検出とクラス推定を別々に行う 2, 相対的に精度が高い傾向 3, 相対的に計算量が大きく推論も遅い傾向 引用:Deep Learning for Generic Object Detection: A Survey 1段検出器(One-stage detector) DetectorNet、YOLO、SSD、YOLO9000、RetinaNet、CornerNet 1, 候補領域の検出とクラス推定を同時に行う 2, 相対的に精度が低い傾向 3, 相対的に計算量が小さく推論も早い傾向 歴史 引用:Deep Learning for Generic Object Detection: A Survey DCGAN DCGANはGANを改良したモデルなので、まずはGANのモデル構造について考えていきます。 GAN(Generative Adversarial Nets) GAN:I. J. Goodfellow, J. Pouget-Abadie, M. Mirza, B. Xu, D. W. Farley, S. Ozair, A. C. Courville, and Y. Bengio. Generative adversarial nets. In Neural Information Processing Systems(NIPS), pp. 2672-2680, 2014. Generator : 乱数からデータを生成 Discriminator : 入力データが真データ(学習データ)であるかを識別 ラビットチャレンジ講義資料より引用 2プレイヤーのミニマックスゲーム 一人が自分の勝利する確率を最大化する作戦を取る もう一人は相手が勝利する確率を最小化する作戦を取る GANでは価値観数Vに対し、Dが最大化、Gが最小化を行う \begin{gathered} \min _{G} \max _{D} V(D, G) \\ V(D, G)=\mathbb{E}_{x \sim p_{\text {data }}(x)}\left[\log _{a} D(x)\right]+\mathbb{E}_{z \sim p_{z}}(z)[\log (1-D(G(z)))] \end{gathered} この式はバイナリークロスエントロピーそのものなので、単純化して説明できます。 バイナリークロスエントロピー $$ L=-\sum y \log \hat{y}+(1-y) \log (1-\hat{y}) $$ 単一データのバイナリークロスエントロピー $$ L=-y \log \widehat{y}+(1-y) \log (1-\widehat{y}) $$ 真データを扱うとき $$ y=1, \hat{y}=D(x) \Rightarrow L=-\log [D(x)] $$ 生成データを扱うとき $$ y=0, \hat{y}=D(G(z)) \Rightarrow L=-\log [1-D(G(z))] $$ 二つの式を足し合わせると、以下の式のように表せます。 \begin{aligned} &L=-(\log [D(\boldsymbol{x})]+\log [1-D(G(\boldsymbol{z}))]) \\ &V(D, G)=\mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}(\boldsymbol{x})}[\log D(\boldsymbol{x})]+\mathbb{E}_{\boldsymbol{z} \sim p_{z}(\mathbf{z})}[\log (1-D(G(\mathbf{z})))] \end{aligned} 複雑なデータを扱うために期待値を取ります。 最適化方法 Generatorのパラメータ$\theta_{g}$を固定 真データと生成データをm個ずつサンプル $\theta_{d}$を勾配上昇法(Gradient Ascent)で更新 $$ \frac{\partial}{\partial \theta_{d}} \frac{1}{m}[\log [D(\boldsymbol{x})]+\log [1-D(G(\boldsymbol{z}))]] $$ Discriminatorのパラメータ$\theta_{d}$を固定 生成データをm個ずつサンプル $\theta_{g}$を勾配降下法(Gradient Descent)で更新 $$ \frac{\partial}{\partial \theta_{g}} \frac{1}{m}[\log [1-D(G(\mathbf{z}))]] $$ 生成データが本物のデータとそっくりな状況とは$p_{g}=p_{\text {data }}$であるはずなので、価値観数が$p_{g}=p_{\text {data }}$の時に最適化されていることを示せれば良いので、以下の2つのステップで確認します。 \min _{G} \max _{D} V(D, G)=\mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}(\boldsymbol{x})}[\log D(\boldsymbol{x})]+\mathbb{E}_{\boldsymbol{z} \sim p_{\boldsymbol{z}}(\boldsymbol{z})}[\log (1-D(G(\mathbf{z})))] 1, Gを固定して価値観数が最大値をとる時のD(x)を算出する Generatorを固定 \begin{aligned} V(D, G) &=\mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}(\boldsymbol{x})}[\log D(\boldsymbol{x})]+\mathbb{E}_{\mathbf{z} \sim p_{z}(\mathbf{z})}[\log (1-D(G(\mathbf{z})))] \\ &=\int_{\boldsymbol{x}} p_{\text {data }}(\boldsymbol{x}) \log (D(\boldsymbol{x})) d x+\int_{z} p_{z}(\mathbf{z}) \log (1-D(G(\mathbf{z}))) d z \\ &=\int_{\boldsymbol{x}} p_{\text {data }}(\boldsymbol{x}) \log (D(\boldsymbol{x}))+p_{g}(\boldsymbol{x}) \log (1-D(\boldsymbol{x})) d x \end{aligned} $\mathrm{y}=D(\boldsymbol{x}), a=p_{\text {data }}(\boldsymbol{x}), b=p_{g}(\boldsymbol{x})$とすれば $$ a \log (y)+b \log (1-y) $$ $a \log (y)+b \log (1-y)$の極値を求めます。 $a \log (y)+b \log (1-y)$をyで微分 \begin{aligned} &\frac{a}{y}+\frac{b}{1-y}(-1)=0 \\ &\frac{a}{y}=\frac{b}{1-y} \\ &a-a y=b y \\ &a=(a+b) y \\ &y=\frac{a}{a+b} \end{aligned} $\mathrm{y}=D(\boldsymbol{x}), a=p_{\text {data }}(\boldsymbol{x}), b=p_{g}(\boldsymbol{x})$なので D(\boldsymbol{x})=\frac{p_{\text {data }}(\boldsymbol{x})}{p_{\text {data }}(\boldsymbol{x})+p_{g}(\boldsymbol{x})} でD(x)が上記の値をとる時、価値観数を最大化することがわかります。 2, 上記のD(x)を価値関数に代入してGが価値観数を最小化する条件を算出する 価値観数のD(x)を$\frac{p_{\text {data }}(x)}{p_{\text {data }}(x)+p_{g}(x)}$で置き換え \begin{aligned} V &=\mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}} \log \left[\frac{p_{\text {data }}(\boldsymbol{x})}{p_{\text {data }}(\boldsymbol{x})+p_{g}(x)}\right]+\mathbb{E}_{\boldsymbol{x} \sim p_{g}} \log \left[1-\frac{p_{\text {data }}(\boldsymbol{x})}{p_{\text {data }}(\boldsymbol{x})+p_{g}(x)}\right] \\ &=\mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}} \log \left[\frac{p_{\text {data }}(\boldsymbol{x})}{p_{\text {data }}(\boldsymbol{x})+p_{g}(\boldsymbol{x})}\right]+\mathbb{E}_{\boldsymbol{x} \sim p_{g}} \log \left[\frac{p_{g}}{p_{\text {data }}(\boldsymbol{x})+p_{g}(\boldsymbol{x})}\right] \end{aligned} JSダイバージェンスで二つ(P1, P2)の確率分布がどのくらい近いのか調べます。 J S\left(p_{1} \| p_{2}\right)=\frac{1}{2}\left(\mathbb{E}_{x \sim p_{1}} \log \left(\frac{2 p_{1}}{p_{1}+p_{2}}\right)+\mathbb{E}_{x \sim p_{2}} \log \left(\frac{2 p_{2}}{p_{1}+p_{2}}\right)\right) JSダイバージェンスは、マイナスにならず、分布が一致する時の0を取ります。 -> 最小値が0 この距離測れるJSダイバージェンスを価値観数の中から取り出します。 価値観数を変形 \begin{aligned} V &=\mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}} \log \left[\frac{p_{\text {data }}(\boldsymbol{x})}{p_{\text {data }}(\boldsymbol{x})+p_{g}(\boldsymbol{x})}\right]+\mathbb{E}_{\boldsymbol{x} \sim p_{g}} \log \left[\frac{p_{g}}{p_{\text {data }}(\boldsymbol{x})+p_{g}(\boldsymbol{x})}\right] \\ &=\mathbb{E}_{\boldsymbol{x} \sim p_{\text {data }}} \log \left[\frac{2 p_{\text {data }}(\boldsymbol{x})}{p_{\text {data }}(\boldsymbol{x})+p_{g}(x)}\right]+\mathbb{E}_{\boldsymbol{x} \sim p_{g}} \log \left[\frac{2 p_{g}}{p_{\text {data }}(\boldsymbol{x})+p_{g}(\boldsymbol{x})}\right]-2 \log 2 \\ &=2 J S\left(p_{\text {data }} \| p_{g}\right)-2 \log 2 \end{aligned} $\min {G} V$は$p{\text {data }}=p_{g}$の時に最小値となります。 以上の学習過程からGANは本物のようなデータを生成できます。 学習ステップの可視化 ラビットチャレンジの講義資料引用 GANによる応用技術 Fast Bi-layer Neural Synthesis of One-Shot Realistic Head Avatars 1枚の顔画像から動画像を高速に生成するモデル 初期化部:人物の特徴を抽出(1アバターにつき一回の計算コスト) 推論部:所望の動きをつける(時間フレーム分だけの計算コスト) 計算コストに関して 従来:初期化の計算コストが小さく、推論部の計算コストが大きい 提案:初期化の計算コストが大きく、推論部の計算コストが小さい(つまりリアルタイムで推論できる) 推論部の計算コストの削減方法は綿密な輪郭(初期化時に輪郭情報を生成:ポーズに依存)と粗い顔画像(推論時に洗い動画像を生成:ポーズに依存)を別々に生成し結合します。 3つのネットワーク構造により構成されています。 1, Embedder 2, Texture Generator 3, Inference Generator DCGAN(Deep Convolutional GAN) GANを利用した画像生成モデルです。GANと比較して幾つかの構造的制約を適用することにより生成品質を向上させています。 DCGAN:A. Radford, L. Metz, and S. Chintala. Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks. International Conference on Learning Representations(ICLR), 2016. Generator Pooling層の代わりに転移畳み込みを使用(乱数を画像にアップサンプリング) 最終層はtanh、その他はReLU関数で活性化 Descriminator Pooling層の代わりに畳み込みを使用(画像の特徴を抽出し、最終層をsigmoid関数で活性化) Leaky ReLU関数で活性化 共通点 中間層に全結合層を使用しない バッチノーマライゼーションを適用
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

仮想空間上でアバターに触れるシステム

こんにちは。ナルキタです。 現在私はVRとタッチケアを合わせた研究に携わっています。 今回はその概要を軽く紹介したいと思います。 まず使用機器一覧です。 Oculus Quest 2 Unityインストール済みのPC Webカメラ マイク マネキン まずは上記の機器を以下の図のように配置します。 上図の様に、ユーザにOculusを装着してもらい、VRアプリを実行します。 この時に、目の前のマネキンと仮想空間上のアバターの座標が合致するように注意します。 座標調整が済んだらVRアプリを実行します。 アプリ実行後、ユーザの目の前のアバターが椅子に座ります。ユーザは仮想空間上でアバターに触れたり撫でることができます。仮想空間上の手は、Oculusのハンドトラッキングによって表示されています。 またユーザの手の動きを後ろからwebカメラで撮影し、ハンドトラッキング及び手の座表を取得します。 上の画像では手の座標を緑の点で示しています このように、仮想空間上のアバターをユーザが触れたり撫でることができるVRアプリを制作致しました。 手の速度を検出するシステムは少々複座鵜ですが、それ以外は簡単なアセットで構成されているUnityシーンですので、皆さんもぜひお試しくださいませ。 それでは~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MVTecAD データセットローダー TensorFlow

はじめに MVTecADデータセットローダープログラム。 プログラム①:MVTecAD クラス Data_Loader.py # MVTec AD Data Loader # ----------------------------------------------- # Import # ----------------------------------------------- # 01. Built-in import os # 02. Third-party modules import cv2 import numpy as np import tensorflow as tf # ----------------------------------------------- # Class # ----------------------------------------------- class MVtecADLoader(): def __init__(self, img_size = 224): # Datasets Path path_notebook = 'C:/Users/user_name/' path_nokori = 'Google ドライブ/datasets/mvtec_anomaly_detection' self.base_path = os.path.join(path_notebook, path_nokori) # Image Resolution self.img_size = img_size # number self.num_train = None # Category NG List self.category = {'bottle': ['good', 'broken_large', 'broken_small', 'contamination'], 'cable': ['good', 'bent_wire', 'cable_swap', 'combined', 'cut_inner_insulation', 'cut_outer_insulation', 'missing_cable', 'missing_wire', 'poke_insulation'], 'capsule': ['good', 'crack', 'faulty_imprint', 'poke', 'scratch', 'squeeze'], 'carpet': ['good', 'color', 'cut', 'hole', 'metal_contamination', 'thread'], 'grid': ['good', 'bent', 'broken', 'glue', 'metal_contamination', 'thread'], 'hazelnut': ['good', 'crack', 'cut', 'hole', 'print'], 'leather': ['good', 'color', 'cut', 'fold', 'glue', 'poke'], 'metal_nut': ['good', 'bent', 'color', 'flip', 'scratch'], 'pill': ['good', 'color', 'combined', 'contamination', 'crack', 'faulty_imprint', 'pill_type', 'scratch'], 'screw': ['good', 'manipulated_front', 'scratch_head', 'scratch_neck', 'thread_side', 'thread_top'], 'tile': ['good', 'crack', 'glue_strip', 'gray_stroke', 'oil', 'rough'], 'toothbrush': ['good', 'defective'], 'transistor': ['good', 'bent_lead', 'cut_lead', 'damaged_case', 'misplaced'], 'wood': ['good', 'color', 'combined', 'good', 'hole', 'liquid', 'scratch'], 'zipper': ['good', 'broken_teeth', 'combined', 'fabric_border', 'fabric_interior', 'rough', 'split_teeth', 'squeezed_teeth']} def load(self, category, repeat = 4, max_rot = 10): #--------------------------------------------- # 01. Load Train Dataset (OK) # --------------------------------------------- #data, mask, binary anomaly label ( 0 for anomaly, 1 for good) x, y, z = [], [], [] path = os.path.join(os.path.join(self.base_path, category), 'train/good') files = os.listdir(path) zero_mask = np.zeros(shape = (self.img_size, self.img_size), dtype = np.float32) for rdx in range(repeat): for file in files: # x, data img_full_path = os.path.join(path, file) img = self._read_image(img_path= img_full_path) #(RGB) if not max_rot == 0: img = tf.keras.preprocessing.image.random_rotation(img, max_rot) # y, mask img_mask = zero_mask # z, binary ( 0 for anomaly, 1 for good) z_element = 1 x.append(img) y.append(img_mask) z.append(z_element) x = np.asarray(x) y = np.asarray(y) z = np.asarray(z) self.num_train = len(x) x = tf.data.Dataset.from_tensor_slices(tf.convert_to_tensor(x, dtype = tf.float32)) y = tf.data.Dataset.from_tensor_slices(tf.convert_to_tensor(y, dtype = tf.int32)) z = tf.data.Dataset.from_tensor_slices(tf.convert_to_tensor(z, dtype = tf.int32)) self.train = tf.data.Dataset.zip((x,y,z)) # --------------------------------------------- # 02. Load Test Dataset (OK, NG) # --------------------------------------------- #data, mask, binary anomaly label ( 0 for anomaly, 1 for good) x, y, z = [], [], [] for label in self.category[category]: path = os.path.join(os.path.join(self.base_path, category),'test/{}'.format(label)) files = os.listdir(path) for file in files: # x, data img_full_path = os.path.join(path, file) img = self._read_image(img_path = img_full_path) if label =='good': mask = zero_mask idx_z = 1 #label = 1 for good else: mask_path = os.path.join(os.path.join(self.base_path, category), 'ground_truth/{}'.format(label)) mask_path_file = os.path.join(mask_path, '{}_mask.png'.format(file.split('.')[0])) mask = self._read_image(img_path= mask_path_file, flags = cv2.IMREAD_GRAYSCALE) mask = mask/255 idx_z = 0 # label = 0 for NG # mask = mask[16:-16, 16:-16] mask = tf.convert_to_tensor(mask, dtype = tf.int32) x.append(img) y.append(img_mask) z.append(int(idx_z)) x = np.asarray(x) y = np.asarray(y) z = np.asarray(z) self.num_test = len(x) x = tf.data.Dataset.from_tensor_slices(tf.convert_to_tensor(x, dtype = tf.float32)) y = tf.data.Dataset.from_tensor_slices(tf.convert_to_tensor(y, dtype = tf.int32)) z = tf.data.Dataset.from_tensor_slices(tf.convert_to_tensor(z, dtype = tf.int32)) self.test = tf.data.Dataset.zip((x, y, z)) def _read_image(self, img_path , flags=cv2.IMREAD_COLOR): n = np.fromfile(img_path, dtype = np.uint8) img = cv2.imdecode(n, flags) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = cv2.resize(img, dsize = (self.img_size, self.img_size)) # img = img[16:-16, 16:-16, :] #why? rotateするから? return img プログラム②:テストプログラム test.py # ----------------------------------------------- # Import # ----------------------------------------------- # 01. Built-in import os # 02. Third-party modules import cv2 import numpy as np import tensorflow as tf # 03. My Module from Data_Loader import MVtecADLoader # Variables img_size = 224 #Instance loader= MVtecADLoader(com = 'note', img_size = img_size) #------------------------------------------------------ # Data Load #------------------------------------------------------ loader.load(category = 'bottle') #Train Data and Test Data train_set = loader.train.batch(batch_size = 16, drop_remainder = True).shuffle(buffer_size = loader.num_train, reshuffle_each_iteration = True) test_set = loader.test.batch(batch_size = 1, drop_remainder = False) print('train set') for x, y, z in train_set: print(x.shape) print(y.shape) print(z.shape) print('test set') for x, y, z in test_set: print(x.shape) print(y.shape) print(z.shape) 結果 train set (16, 224, 224, 3) #x (16, 224, 224) #y (16,) #z ... test set (16, 224, 224, 3) (16, 224, 224) (16,) ...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】Airflow ローカルで立ち上げ

概要 Pythonのワークフロー管理ツールであるAirflowをローカルPCで立ち上げまで行う。 Airflowとは はじめにAirflow とは何かを説明する。 正式名称: Apache Airflow Airflowとは、Airbnbではじめ開発がスタートされた一連のタスクフローを管理できるOSSのPythonフレームワークである。 もとは、FaceBook社内で使われていたDataswarm(コード非公開)と呼ばれるワークフロー管理ツールの開発者、Maxime BeaucheminがAirbnbに移った後、開発を始めたソフトウェアであるためその時の名残が大きいようである。 各Taskは、Digというファイル内で記述され、一連のワークフローの依存関係もファイル内にPythonコードとして定義する。 OSS自体もPure Pythonで書かれている。 GUIツール上からワークフローの設定変更や実行など行え、比較的UIも洗練されている。 Python上で分岐処理の定義をしておけば、一連のフローをGUI上で確認することもできて便利。 フローをっコーディングで記述できるので拡張性が高い。 環境 環境 バージョン OS macOS Mojave 10.14.6 Docker compose 1.29.1 Airflow 2.2.3 立ち上げの流れ 基本、公式ドキュメントからDockerでの実行方法をベースに立ち上げを行う。 Running Airflow in Docker 気をつけるべきこと Docker Desktop の設定から割当可能なMemoryを上げる。 デフォルトでは、2GBであるが、最低4GB, 推奨8GBまであげないとAirflowが立ち上がらない。 自分の場合は、12GBまで上げました。 以下はOSがLinuxのとき実行するとあるがmacOSにおいても実行しないと正常にDockerコンテナが立ち上がらない。 echo -e "AIRFLOW_UID=$(id -u)" > .env ブラウザ上でアクセスする http://localhost:8080/ 所感 前処理や無数のパラメータ調整が必須で管理が煩雑になりがちな機械学習モデルの実験管理に役立ちそう。研究系のタスクがメインの方面でも使われそう。 機械学習系の現場の本番環境においても、実データを前処理してETLにデータを突っ込む一連の処理の流れをPythonベースで定義できるので重宝しそう。 Web開発の現場でもバッチ処理を諸々管理できるので、いろんな使いみちがありそう。 ローカルだと少し重め。問題あるレベルではないが。 これからより触って理解を深めていきたいところです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

python subprocessで標準出力もエラーもファイル出力する

greeting.py import argparse import time def arg_res(): parser = argparse.ArgumentParser() parser.add_argument('--nickname',required=True) args = parser.parse_args() nickname = args.nickname # エラーなし greeting_sentence = f"僕のニックネームは{nickname}" print(greeting_sentence) # エラーあり greeting_sentence = f"僕のニックネームは{nicknames}" print(greeting_sentence) return greeting_sentence def test_main(): greeting_sentence = arg_res() print(greeting_sentence) if __name__=="__main__": test_main() run_greeting.py import subprocess from time import time name_list = ["john","power","smith","jack"] with open('out.txt', 'w') as fp: for i in name_list: print(i) # コンソール実行時の場合に空白で区切る部分を分割してリストに入れる" # python greeting.py --nickname XXXX の場合下記のようになる。 command = ["python","greeting.py","--nickname", f"{i}"] proc = subprocess.Popen(command, stdout=fp,stderr=fp) 出力結果 out.txt 僕のニックネームはsmith 僕のニックネームはjohn 僕のニックネームはpower Traceback (most recent call last): File "greeting.py", line 28, in <module> test_main() File "greeting.py", line 24, in test_main greeting_sentence = arg_res() File "greeting.py", line 17, in arg_res greeting_sentence = f"僕のニックネームは{nicknames}" NameError: name 'nicknames' is not defined Traceback (most recent call last): File "greeting.py", line 28, in <module> test_main() File "greeting.py", line 24, in test_main greeting_sentence = arg_res() File "greeting.py", line 17, in arg_res greeting_sentence = f"僕のニックネームは{nicknames}" NameError: name 'nicknames' is not defined Traceback (most recent call last): File "greeting.py", line 28, in <module> test_main() File "greeting.py", line 24, in test_main greeting_sentence = arg_res() File "greeting.py", line 17, in arg_res greeting_sentence = f"僕のニックネームは{nicknames}" NameError: name 'nicknames' is not defined 僕のニックネームはjack Traceback (most recent call last): File "greeting.py", line 28, in <module> test_main() File "greeting.py", line 24, in test_main greeting_sentence = arg_res() File "greeting.py", line 17, in arg_res greeting_sentence = f"僕のニックネームは{nicknames}" NameError: name 'nicknames' is not defined
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【python】文字の出力

pythonの文字の出力方法です。 初投稿なのでお手柔らかに。 基本編 ①文字列の出力 ダブルクォーテーション、またはシングルクォーテーションで挟むことで文字列の出力が出来ます。逆に挟まないとエラーになります。 string.py print("Hello") 出力結果 Hello ②数値の出力 数値の場合は、クォーテーションで挟んでも挟まなくても出力できます。 python:int.py print(1) 出力結果 1 応用編 ①複数の文字列の出力 stringAndInt.py print("真実は" + "いつも1つ") 出力結果 進撃の巨人 ②複数の数値の出力 stringAndInt.py print(1 + 2 + 3) 出力結果 123 ③文字列と数値の出力 stringAndInt.py print("欅坂" + str(46)) 出力結果 欅坂46
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Sympyのシンボルの取りうる値を定義するAssumptionsについて

SymPyのAssumptionsとは import sympy x = sympy.Symbol('x') y = sympy.sqrt(x**2) print(sympy.simplify(y)) # sqrt(x**2) デフォルトでSymPyのシンボルを定義した上記の場合、 $x$ は任意の複素数として解釈される。そのため、simplify() により数式 $y$ の簡略化を実行しても、正の実数の時に成立する $y = \sqrt{x^2} \Rightarrow y = x$ という変換は行われない。 import sympy x = sympy.Symbol('x', positive=True) y = sympy.sqrt(x**2) print(sympy.simplify(y)) # x シンボルの定義の際、引数に positive=True を与えることで、$x$ が正の実数として解釈され、正の実数の時に成立する $y = \sqrt{x^2} \Rightarrow y = x$ の変換が行われる。 このように、シンボルを定義する際にそのシンボルが取りうる値を定義することで、数式の変換などの処理が適切に行われる。このような定義のことをSymPyではAssumptionsと呼んでいる。 指定可能なAssumptionsの一覧 シンボルに指定できるAssumptionsは下記のようなものがある。 詳細は公式ドキュメントの一覧を参照のこと。 Assumption predicate 日本語名称 集合 commutative 可換性 infinite 無限 $ \{-\infty, +\infty, \tilde{\infty}\} $ 1 finite 有限 hermitian エルミート演算子の要素 antihermitian 反エルミート演算子の要素 complex 複素数 $ z \in \mathbb{C} $ algebraic 代数的数 $ z \in \mathbb{\overline{Q}} $ transcendental 超越数 $ z \in \mathbb{C} - \mathbb{\overline{Q}} $ extended_real 拡大実数 $ x \in \mathbb{\overline{R}} $ $ (\mathbb{\overline{R}} = \mathbb{R} \cup \{-\infty, +\infty\}) $ real 実数 $ x \in \mathbb{R} $ imaginary 純虚数 $ z \in \mathbb{I} - \{0\} $ rational 有理数 $ x \in \mathbb{Q} $ irrational 無理数 $ x \in \mathbb{R} - \mathbb{Q} $ integer 整数 $ x \in \mathbb{Z} $ noninteger 非整数拡大実数 $ x \in \mathbb{\overline{R}} - \mathbb{Z} $ even 偶数 $ \{2k \mid k \in \mathbb{Z}\} $ odd 奇数 $ \{2k+1 \mid k \in \mathbb{Z}\} $ prime 素数 $ x \in \mathbb{P} $ composite 合成数 $ x \in \mathbb{N} - (\mathbb{P} \cup \{1\}) $ zero ゼロ $ \{0\} $ nonzero 非ゼロの実数 $ x \in \mathbb{R} - \{0\} $ extended_nonzero 非ゼロの拡大実数 $ x \in \mathbb{\overline{R}} - \{0\} $ positive 正の実数 $ \{x \in \mathbb{R} \mid x \gt 0\} $ nonnegative 非負の実数 $ \{x \in \mathbb{R} \mid x \ge 0\} $ negative 負の実数 $ \{x \in \mathbb{R} \mid x \lt 0\} $ nonpositive 非正の実数 $ \{x \in \mathbb{R} \mid x \le 0\} $ extended_positive 正の拡大実数 $ \{x \in \mathbb{\overline{R}} \mid x \gt 0\} $ extended_nonnegative 非負の拡大実数 $ \{x \in \mathbb{\overline{R}} \mid x \ge 0\} $ extended_negative 負の拡大実数 $ \{x \in \mathbb{\overline{R}} \mid x \lt 0\} $ extended_nonpositive 非正の拡大実数 $ \{x \in \mathbb{\overline{R}} \mid x \le 0\} $ $\tilde{\infty}$ はComplexInfinity https://docs.sympy.org/latest/modules/core.html#complexinfinity ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Google ColaboratoryでopenCV顔検出によるフェイスルビ(ファイル参照とURLローダー両方に対応)

人物の顔の検出順にインクリメントします。 動作 GIF動画 Bgore: After: ファイル参照は複数アップロードできますが、最初の一枚が対象です。 URLローダーは 429 Too Many Requests が出ることがあります。 jpg、png対応。 機能はインクリメントのみです。 cascadeは正面顔のみです。 ノートブック フェイスルビ 使い方 メニューバーから、 ランタイム > すべてのセルを実行 画像URLから読み込みたい場合は、事前に2枚目のコードセルの TARGET に画像のURLを入れます。 TARGET が空の場合は ファイル参照が起動します。 そもそもGoogle Colaboratoryの使い方は、という方はこちら。 ソース コードセル1 #初期化 !rm -i -rf download !mkdir download !cd /content/download &&\ wget https://github.com/opencv/opencv/archive/4.5.4.zip &&\ unzip 4.5.4.zip !cp -R /content/download/opencv-4.5.4/data/haarcascades /content コードセル2 #@title 画像URLから読み込む。 TARGET = "" #@param {type:"string"} from io import BytesIO import requests import time import mimetypes import IPython import cv2 from PIL import Image from operator import itemgetter # func def get_face_list(image=None,min_size=(30,30)): image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cascade_file = "/content/haarcascades/haarcascade_frontalface_alt2.xml" cascade = cv2.CascadeClassifier(cascade_file) face_list = list(cascade.detectMultiScale(image_gray, minSize=min_size)) face_list.sort(key=itemgetter(0)) return face_list def assign_a_number(image,output_path): face_list=get_face_list(image) font = cv2.FONT_HERSHEY_PLAIN if len(face_list): i=0 for (x,y,w,h) in face_list: i+=1 fontsize=int((w/10)/1.5) fontweight=int(w/10) x=x+w if x-w<0 else x-int(w/3) y=y+h*2 if y-h<0 else y-int(h/3) cv2.putText(image, f"{i}", (x,y), font, 1 if fontsize==0 else fontsize, (0, 0, 255), 1 if fontweight<1 else fontweight) cv2.imwrite(output_path,image) else: print("not detected") def tmp_display(img_path): fo=IPython.display.Image(img_path) if fo.format=="jpeg": IPython.display.display_jpeg(fo) elif fo.format=="png": IPython.display.display_png(fo) # uploader from google.colab import files if not TARGET: TARGET=[] uploaded = files.upload() for fn in uploaded.keys(): TARGET.append(f"/content/{fn}") else: img=Image.open(BytesIO(requests.get(TARGET,timeout=15).content)) out_ex=mimetypes.guess_extension(f"image/{img.format}") fn=f"{int(time.time())}{out_ex}" img.save(f"/content/{fn}",img.format) TARGET=[f"/content/{fn}"] # img_path=TARGET[0] print("Before: ") tmp_display(img_path) print("") assign_a_number(cv2.imread(img_path),img_path) print("After: ") tmp_display(img_path) TARGET="" !rm -f *.jpg *.png 参考 フリー画像
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Django REST Framework】外部APIをAPIViewで取得する

Django REST FrameworkのAPIViewを使って外部APIを呼び出してみます。 今回はFinancial Modeling PrepをのSearchAPI(株式のシンボルを探すAPI)を使用しています。 <環境変数> .env ⇒ settings.py ⇒ モジュール 1.モジュールの作成 プロジェクト/lib/fmp_api.py from django.conf import settings import requests API_KEY = settings.FMP_API_KEY FMP_V3 = settings.FMP_V3 def search_symbol(keyword, limit=10, exchange=None): url = FMP_V3 + 'search' query = { 'query': keyword, 'limit': limit, 'apikey': API_KEY } if exchange: query['exchange'] = exchange r = requests.get(url, params=query, timeout=2.0) if r.status_code == 200: data = r.json() return data else: return None キーワードを指定すると10個のシンボルが返ってきます。 取引所の指定をする場合はexchangeを指定します。 2.シリアライザの作成 アプリ/api/v1/views.py from rest_framework import serializers from profiles.models import FavoStock class FMPSearchSymbolSerializer(serializers.Serializer): symbol = serializers.CharField(max_length=20) name = serializers.CharField(max_length=100) currency = serializers.CharField(max_length=3) stockExchange = serializers.CharField(max_length=100) exchangeShortName = serializers.CharField(max_length=15) user_has_liked_symbol = serializers.SerializerMethodField() def get_user_has_liked_symbol(self, instance): request = self.context.get('request') return FavoStock.objects.filter(profile=request.user.profile, symbol=instance['symbol']).exists() お気に入りは別アプリのモデルからインポート。 登録があればtrue、なければfalseで返ってきます。 3.クラスベースビューの作成 アプリ/api/v1/views.py from rest_framework.permissions import IsAuthenticated from rest_framework.views import APIView from rest_framework import status from rest_framework.response import Response from プロジェクト名.lib import fmp_api from stocks.api.v1.serializers import FMPSearchSymbolSerializer class SearchFMPSymbolAPIView(APIView): permission_classes = [IsAuthenticated] def get(self, request,kw): data = fmp_api.search_symbol(kw) if data: results = FMPSearchSymbolSerializer(data, many=True, context={"request": request}) return Response(results.data) else: return Response( { "error": { "code": 404, "message": "not found" } }, status = status.HTTP_404_NOT_FOUND ) 日本語で探すとnot foundでレスポンスされます。 4.URLの設定 アプリ/api/v1/urls.py from django.urls import path, include from stocks.api.v1.views import SearchFMPSymbolAPIView urlpatterns = [ path('search/<str:kw>/', SearchFMPSymbolAPIView.as_view(), name='search-symbol'), ] あとはプロジェクトのurls.pyにアプリのurlsを追加して終了です。 結果 結果が出力される場合↓ エラーの場合↓ 参考 DjangoでAPI呼び出し Django rest framework & external api
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

2値画像からピクセルが存在する部分だけ切り抜いてリサイズする

方針 ピクセルが0, 1の2値を取る画像から ピクセルが存在する上下左右を使って切り抜き、元のサイズに戻す 行、列ごとに値が1であるピクセルが何個か求める 1が1つ以上である最大値、最小値を行、列ごとに求める それぞれを上下左右に対応 トリミング cv2でリサイズ 使用する関数 np.count_nonzero(条件式, axis=0か1) 条件に合致する行、列の要素数を返す axis=0なら列、1なら行 np.where(条件式) 条件に合致するIndexを返す 作ったもの arr = np.load("path") col = np.count_nonzero(arr == 1, axis=0) row = np.count_nonzero(arr == 1, axis=1) top = np.where(row>0)[0][0] bottom = np.where(row>0)[0][-1] left = np.where(col>0)[0][0] right = np.where(col>0)[0][-1] trimed_arr = arr[top:bottom+1, left:right+1] resized_arr = cv2.resize(trimed_arr.astype('uint8'), dsize=arr.shape)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python備忘録

ファイル操作 ディレクトリ・ファイルが存在するか os.path.exists ディレクトリが存在するか os.path.isdir ディレクトリを再帰的に作成 os.makedirs('dirname', exist_ok=True) numpy 保存・読み出し numpy配列を保存 np.save("dir",arr) numpy配列を読み込み np.load("dir") 型 型を指定してnumpy配列を作成 np.array(arr, dtype="") numpy配列を型キャスト [ndarray].astype([dtype]) matplotlib 保存・読み出し numpy配列を画像として保存 plt.imsave("dir", arr)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCodeにおいてpytestでdebugを実行した際、エラー箇所でbreakしないことについて

課題 VSCodeにおいてpytestでdebugを実行した際、エラー箇所でbreakしないこと。 画像は以下URLから参照 https://stackoverflow.com/questions/62419998/how-can-i-get-pytest-to-not-catch-exceptions/62563106#62563106 解決方法 Debugの画面で、BREAKPOINTSの「User Uncaught Exceptions」にチェックボックスを入れるだけ。 ここに解決方法が書いてありました。 https://github.com/microsoft/debugpy/issues/364
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonで文字列からBool型に変換する方法

Background str = "True" と文字列になっている値をBoolにしたい場合があります。 直感的にbool(str) にしたいのですが思った結果が出ません。ここでは、TrueやFalseなどの文字列からBool型にする方法を紹介します。 組み込み関数 bool 組み込み関数として bool が存在します。 "True" となっているものは True(bool)に、 "False" となっているものは False(bool)に変換しそうですが実際は違います。 なぜなら、bool関数は入力値が空文字以外はTrueと出力され、空文字の場合はFalseと出力されます。そのため、bool("False")としても空文字以外と判定されてしまうためTrueと出力されます。 >>> str_true = "True" >>> str_false = "False" >>> bool(str_true) True >>> bool(str_false) True Method 解決方法として distutils.util パッケージ内の strtobool関数を使います。 公式のドキュメントを見ると、 真偽値をあらわす文字列を真(1)または偽(0)に変換します。 真の値は y, yes, t, true, on そして 1 です。偽の値は n, no, f, false, off そして 0 です。 val が上のどれでもない時は ValueError を起こします。 と書いてあります。 Pythonでは Trueは 1、 Falseは0と同義なのでstrtoboolをそのまま使って問題ないです。 Development >>> from distutils.util import strtobool >>> str_true = "True" >>> str_false = "False" >>> strtobool(str_true) 1 >>> strtobool(str_false) 0 #数字だと不安に思う人はbool関数でラッピングする >>> bool(strtobool(str_true)) True >>> bool(strtobool(str_false)) False びっくりすることに、上記でチラッと書いた通り、yes/noやon/offもTrue/False(bool)に変換してくれます。 >>> str_yes = "Yes" >>> str_no = "No" >>> str_on = "On" >>> str_off = "Off" >>> bool(strtobool(str_yes)) True >>> bool(strtobool(str_no)) False >>> bool(strtobool(str_on)) True >>> bool(strtobool(str_off)) False Supplement 開発するひとによってはTRUE True trueといろんな書き方のパターンがあります。どれでも対応できるのか確認すると、strtobool内で引数にlowerを使って小文字に変換しています。なのでどれでもOKです。 def strtobool (val): """Convert a string representation of truth to true (1) or false (0). True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 'val' is anything else. """ val = val.lower() if val in ('y', 'yes', 't', 'true', 'on', '1'): return 1 elif val in ('n', 'no', 'f', 'false', 'off', '0'): return 0 else: raise ValueError("invalid truth value %r" % (val,)) Reference Pythonの真偽値bool型(True, False)と他の型との変換・判定 文字列からbool値に変換する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

登山好きプログラミング素人が衛星データで登りたい山を探してみた!〜②衛星データで周囲で一番高い山を探してみる編〜

はじめに ・登山好きプログラミング初心者が衛星データを利用して山探しする企画です。 ・こう書いた方がより良いなどありましたら、勉強になりますのでぜひコメントください。 ・本企画は以下の4部構成を予定しており、今回は第2弾です。 ①日本の山リスト作成編 ②衛星データで周囲で一番高い山を探してみる編 ③衛星データで山周辺の地域の晴天率を調べてみる編 ④衛星データで眺望の良い、山頂がひらけている山を探してみる編 前回記事①日本の山リスト作成編で作成した日本の山リストに対して、標高の衛星データを使って自分たちの登りたい山を絞っていきます。 今回は以下の二つの条件で、登りたい山を絞っていきます。 ・標高2000m以上 ・周辺で最も高い山 標高2000m以上の山を絞る 標高2000m以上の山を絞ります。 衛星データを用いて絞ることもできますが、前回記事で作成したcsvに標高の値があるため、今回はそちらを使って絞ります。 まずは前回記事で作成した日本の山リストのcsvを読み込みます。 その後、標高の列の値が2000以上のものを抽出します。 抽出後は行数の値が飛び飛びになってしまっているため、reset_index()で番号を振り直します。 最後にcsvで保存します。 df = pd.read_csv("/content/drive/My Drive/mountain_list_fin.csv", index_col=0) # 標高2000m以上を抽出 df2000 = df[df['標高'] >= 2000] df2000.reset_index(drop=True, inplace=True) df2000.to_csv("/content/drive/My Drive/mountain_list_2000.csv") この結果を前回と同じようにGoogleマップに表示してみます。 前回の結果から山が絞られた様子が分かります。 驚いたことに2000m以上の山は西日本にはないんですね… 周辺で最も高い山の絞り方 山頂に着いたときに、近くに自分がいる位置より高い山があるのはなんか悔しいですよね。 そのために自分が周辺で最も高い山を衛星データを用いて絞っていきます。 以下の図のように山頂を中心に大小二つの正方形の領域で標高データを取得し、その最大値を比較します。最大値が同じだった場合には青い領域でも、中心の山頂が最も標高が高かったことになるため、この山は周囲で最も高い山であると言えます。逆に最大値が異なっていた場合は、青い領域に中心の山頂以上に高い標高の場所があると言えるため、この山は周囲で最も高い山ではないことがわかります。 この方法を用いて、周囲で最も高い山を絞ります。 標高データの紹介 今回使用する衛星データは「ALOS DSM Global 30m」です。 https://developers.google.com/earth-engine/datasets/catalog/JAXA_ALOS_AW3D30_V3_2 こちらのデータのDSMというバンドで標高データを取得することができます。各衛星には複数のバンドがあり、バンド名は上記のGEEのデータカタログにて調査が可能です。 まず試しに富士山を中心として画像を取得し表示してみます。 GEEのデータを使用するため、GEEの認証とモジュールのインポートを行います。 GEE認証を実行すると認証に必要なURLが表示されるので、URLへアクセスしてGoogleアカウントを指定し、表示された認証コードをGoogle Colabのボックスへコピペしてください。 !pip install rasterio #初めて実行するときのみ import ee import numpy as np import matplotlib.pyplot as plt import rasterio try: ee.Initialize() except Exception as e: ee.Authenticate() ee.Initialize() 衛星データをロードする関数を定義します。 衛星名(snippet)、場所(geometry)、バンド(band)を指定して、衛星データをロードします。通常はどの期間のデータをロードするのかも指定しますが、今回使用する標高データはデータ数が一つのため、不要です。 # GEEのデータロード def load_data(snippet, geometry, band): # パラメータの条件にしたがってデータを抽出 dataset = ee.ImageCollection(snippet).filter( ee.Filter.geometry(geometry)).select(band) # リスト型へ変換 data_list = dataset.toList(dataset.size().getInfo()) # 対象データ数とデータリストを出力 return dataset.size().getInfo(), data_list 次に衛星名(snippet)、バンド(band)、関心領域(aoi)を指定します。 場所には富士山を中心として、緯度経度0.2[°]の四角形を指定しています。 ## パラメーターの指定 # 衛星を指定 snippet = 'JAXA/ALOS/AW3D30/V3_2' # バンド名を指定 band = 'DSM' # 富士山周辺を指定 lon = 138.727363 lat = 35.360626 aoi = ee.Geometry.Rectangle([lon - 0.1, lat - 0.1, lon + 0.1, lat + 0.1]) データリストを取得し、データ数を確認します。 今回はデータ数は”1”と表示されます。 ## データ数の確認 num_data, data_list = load_data(snippet=snippet, geometry=aoi, band=band) print('Datasets; ', num_data) 指定したデータをGoogle Drive内にTIFF(.tif)ファイルで保存します。 下記のコードを実行すると、My Drive > fijisan_test > にfuji_muntain.tifというファイルが保存されます。 image = ee.Image(data_list.get(0)) # Gdriveへ保存 task = ee.batch.Export.image.toDrive(**{ 'image': image,# 対象データの指定 'description': 'fuji_mountain',# ファイル名の指定 'folder':'fijisan_test',# Google Drive(MyDrive)のフォルダ名 'scale': 30,# 解像度の指定 'region': aoi.getInfo()['coordinates']# 上記で指定した対象エリア }) # 処理の実行 task.start() データの取得には時間がかかるので、下記コードを実行して、Falseと表示されるまで待ちます。 # Falseと表示されれば処理終了 task.active() 無事データが取得されたら、最大標高を取得し、画像を表示します。 No such file or directoryというエラーが出た場合には、まだデータのダウンロード中か、Google Driveのマウントができていないかと思われます。確認してみてください。 # データの読み込み with rasterio.open('/content/drive/My Drive/fijisan_test/fuji_mountain.tif') as src: arr = src.read() print("最大標高: ", arr.max()) # 可視化 plt.imshow(arr[0]) 以下のように表示されました。 富士山の標高3776mと比べて多少誤差があるものの、無事富士山山頂を中心とする画像の取得ができました。 reducerを用いて画像の最大値を取得する 富士山の例のように、全ての山に対して画像ファイルをダウンロードして、それを読み取る方法で画像の最大値を取得することができます。しかし今回のように、複数の画像を調査したい、代表値のみ取得できれば良い、といった際には”reducer”という機能が便利です。 reducerを使えば、画像をローカルにダウンロードせずに、画像の最大値を取得することができます。reducerを使って富士山の最大標高を取得してみましょう。 先程取得したimageに対してreducerを適用します。 reduceRegion()に、reducer、geometry、scaleを与えることで、画像の代表値を取得することができます。reducerにはmax()、min()、meam()、median()などがあります。 max = image.reduceRegion(reducer=ee.Reducer.max(), geometry=aoi, scale=30) print(max.values().getInfo()[0]) 実行結果は以下のようになりました。 上と同じように”3771”という富士山の最高標高が取得できています。 reducerを用いることで簡単に最大値を取得することができました。 一枚の画像ではあまり効果を感じないかもしれませんが、たくさんの画像に対して処理を行いたい場合にはとても便利な機能です。 周囲で最も高い山を探す いよいよ衛星データを使って登りたい山を絞っていきます。 まずはGEEにログインします。 import ee import pandas as pd import csv try: ee.Initialize() except Exception as e: ee.Authenticate() ee.Initialize() 上と同じように衛星データをロードする関数を定義します。 def load_data(snippet, geometry, band): # パラメータの条件にしたがってデータを抽出 dataset = ee.ImageCollection(snippet).filter( ee.Filter.geometry(geometry)).select(band) # リスト型へ変換 data_list = dataset.toList(dataset.size().getInfo()) # データリストを出力 return data_list 衛星名とバンドを指定します。 ここまでは上と同じです。 ## パラメーターの指定 # 衛星を指定 snippet = 'JAXA/ALOS/AW3D30/V3_2' # バンド名を指定 band = 'DSM' 画像から関心領域の最大値を取得する関数を定義します。 reducerを用いて画像の最大値を取得し、返しています。 def get_max(aoi): data_list = load_data(snippet=snippet, geometry=aoi, band=band) image = ee.Image(data_list.get(0)) max = image.reduceRegion(reducer=ee.Reducer.max(), geometry=aoi, scale=30) return max.values().getInfo()[0] 先ほど作成した標高2000m以上の山リストを基に周辺で最も高い山を絞っていきます。 リストにある山に対して、ループ処理で以下の①〜④を適用していきます。 ①リストから山頂の緯度経度を取得します。 ②緯度経度0.02[°]の正方形の標高データから最大値を取得 ③緯度経度0.2[°]の正方形の標高データから最大値を取得 ④②と③の最大値を比較し、異なっていればリストから削除する df_top = df = pd.read_csv("/content/drive/My Drive/mountain_list_2000.csv", index_col=0) for i in range(len(df_top)): print(i) lat = df_top.at[i, "緯度"] lon = df_top.at[i, "経度"] aoi_inner = ee.Geometry.Rectangle([lon - 0.01, lat - 0.01, lon + 0.01, lat + 0.01]) max1 = get_max(aoi_inner) aoi_outer = ee.Geometry.Rectangle([lon - 0.1, lat - 0.1, lon + 0.1, lat + 0.1]) max2 = get_max(aoi_outer) if max1 != max2: df_top = df_top.drop(i, axis=0) 最後に結果を保存します。 df_top.reset_index(drop=True, inplace=True) df_top.to_csv("/content/drive/My Drive/mountain_list_top.csv") 結果をGoogleマップに表示します。 山は32個に絞られました。 高い山の連なる北アルプス周辺を見てみます。 北アルプス最高峰は奥穂高岳なので、その周辺の常念岳、焼岳、槍ヶ岳などは周囲で一番高い山ではないと判断されていることがわかります。 しかし涸沢岳のようにかなり近くに2000m以上の山がある場合には、内側の小さな長方形内に二つの山が入ってしまい、どちらも残ってしまっています。厳密に絞りたいのであればここは要改善です。 しかし今回は概ね周囲で一番高い山を絞ることができているため、このまま次のステップに進みたいと思います。 まとめ 今回はpandasを用いて2000m以上の山、標高の衛星データを用いて周囲で最も高い山を絞りました。一応良好な結果を得られたのではないでしょうか。 次回からは気象の衛星データを用いて晴天率の高い山、植生指数という衛星データを用いて山頂地点での見晴らしが良い山を探していきます。 ※2022/01/16に次の記事を公開予定です。 参考文献 ※1  GEEデータカタログ:ALOS DSM Global 30m https://developers.google.com/earth-engine/datasets/catalog/JAXA_ALOS_AW3D30_V3_2 ※2 ImageCollection Reductions https://developers.google.com/earth-engine/guides/reducers_image_collection
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

備忘録 2022年1月9日 Python CSVファイルの読み込み unicode error データを可視化する際に見るべきこと

Python CSVファイルの読み込み pythonでのcsvファイルの読み込み unicode error open関数を使うとエラー (unicode error) が出てしまいます C:\\Users\\hoge\\OneDrive\\Documents\\code\\hogehoge これをこうする C:/Users/hoge/OneDrive/Documents/code/hogehoge SyntaxError: invalid non-printable character U+3000 Pythonの「SyntaxError: invalid non-printable character U+3000」とは何ですか? 元のcsvにて全角スペースと半角スペースが混合していたので置換 matplotlibの日本語化(フォント変更) matplotlibの日本語化(フォント変更) PyAutoGUI アラートの表示 PyAutoGUI Message Box Functions alert()は普通のアラート confirm()が?マークのやつ 確認用としてalert()が一番シンプルだと感じた 読み込んだCSVから要素を削除する matplotlibにてグラフのサイズを変更する データの可視化をするとき絶対見るべき
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonのheapqをmax heapとして利用する方法

Pythonの優先度付きキューであるheapqはmin heapです。そのためheappop()すると、最小値がポップされます。max heapとして、最大値をポップしたい場合には、正負反転したリストをheapify()して利用するのが簡単な方法です。 import heapq def test_heapq_maxheap(): A = [1, 6, 8, 0, -1] A = [-1 * a for a in A] heapq.heapify(A) v = -1 * heapq.heappop(A) assert v == 8 これで、min heap(標準)とmax heapを状況に応じて使い分けられます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】Python3.10以降の新機能 match文を使ってみる

はじめに Python3.10 から match文 が搭載されました。 match文 は 従来のif文 と異なり条件式に対応できないという、落とし穴があるのと、 if文と同じように、for文と組み合わせて使うことが多いと思ったので、そこら辺を記載しようかなと思います。 ※注記※ 正規表現にもmatch関数というものがあるので、混在しないように気をつけてください。完全に別物です。 正規表現のmatch関数については以下の記事を参考にしてみてくださいね。 Python 正規表現のmatch関数 動作確認環境 OS: mac OS Monterey 12.1 (M1 Mac) 環境: miniforge (conda環境) Python: 3.10.1 エディタ: Visual Studio Code ※注記※ 必ず、Python 3.10.0以上で実行してください。 環境構築 まずは環境構築から、condaでPython3.10.1の環境を作ります。 ターミナルを開いて # condaでPython3.10.1の環境構築 conda create -n python310 python==3.10.1 conda activate python310 # Python 3.10.1であることを確認 python -V # デスクトップに作業フォルダと空のpythonファイルを作成 cd ~/Desktop mkdir match_test cd match_test touch if.py touch match.py touch match_error.py デスクトップにmatch_testというフォルダーが作成されているので、Visual Studio Codeで開きます。 if.py, match.py, match_error.pyがフォルダーに格納されているので、これらを編集して、実行し動作を確認していきます。 if.py まずは従来の if文 を使った処理を書いてみます。 この if文 の内容を match文 で後ほど書いてみることとします。 list = [1, 2, 3, 4, 5, 6, 8, 10, 11, 13, 15, 17, 19, 20, 22, 23] list_mult2 = [] list_mult3 = [] list_others = [] for i in list: if i % 2 == 0: list_mult2.append(i) elif i % 3 == 0: list_mult3.append(i) else: list_others.append(i) print(list_mult2) print(list_mult3) print(list_others) # 実行結果 [2, 4, 6, 8, 10, 20, 22] [3, 15] [1, 5, 11, 13, 17, 19, 23] リスト、for文、if文 を使っています。 実行すると、2の倍数が表示されて、2の倍数を除いた3の倍数が表示されて、それ以外の数字が表示されます。 3の倍数をすべて表示したい場合は、すこし複雑な処理になるので、ここでは割愛させてください。 match_error.py 上記の if文 処理を match文 で記載しています。 list = [1, 2, 3, 4, 5, 6, 8, 10, 11, 13, 15, 17, 19, 20, 22, 23] list_mult2 = [] list_mult3 = [] list_others = [] for i in list: match i: case i % 2 == 0: list_mult2.append(i) case i % 3 == 0: list_mult3.append(i) case _: list_others.append(i) print(list_mult2) print(list_mult3) print(list_others) # 実行結果 case i % 2 == 0: ^ SyntaxError: expected ':' エラーが表示されれば成功です。 これがif文 と異なる、match文 の落とし穴です。 case のパターンには、演算処理を入れることはできません。 例えば、 i + 1 といった処理でも入れることはできません。 詳しくは以下のリンクを確認してください。 パターンの文法 match.py 先程エラーになってしまったので、ちゃんと出力できるコードにします。 case のパターンで演算処理を含む条件式を入れるには、if文 を使います。 list = [1, 2, 3, 4, 5, 6, 8, 10, 11, 13, 15, 17, 19, 20, 22, 23] list_mult2 = [] list_mult3 = [] list_others = [] for i in list: match i: case i if i % 2 ==0: list_mult2.append(i) case i if i % 3 ==0: list_mult3.append(i) case _: list_others.append(i) print(list_mult2) print(list_mult3) print(list_others) # 実行結果 [2, 4, 6, 8, 10, 20, 22] [3, 15] [1, 5, 11, 13, 17, 19, 23] 正しく表示することができましたね。 caseで if文 を取り入れることで、match文 でも条件処理ができました。 結局 if文 を使っているので、わざわざ match文 を使う必要はなく、if文 の処理で十分だとは思います。 match文のメリット match文は True/False のように、〇〇の値が0の場合、1の場合といった条件分岐に非常に強いと思います。 if文でもこの処理はできますが、match文のほうが見やすく解読しやすいと思いますね。 さいごに 簡単な match文 の紹介でした。 match文 は導入されたばかりで、今後アップデートで条件式が使えるようになったりとか、処理が変化していくかもしれません。 Python のバージョンアップが合った場合には、都度動作を確認してみてくださいね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nishikaコンペ参加記録 小説家になろう ブクマ数予測 ~”伸びる”タイトルとは?~

きっかけ 次のような日本語NLPをやってきました. tfidf/mbart/mt5/CLIP embeddings cluster vis: 特許など長文の、動的な文章間類似可視化・迅速閲覧・解析手段。および第三の特許検索・探索手法 https://qiita.com/kzuzuo/items/8a80d8974bf3a7db7e54 更新中 前) 特許SDI用AI: 複数の自然言語深層学習モデルにおいて見られた個性とその解釈 および認知的観点に基づく知識構造の多様性を評価した価値共創の展望と、創造性  https://qiita.com/kzuzuo/items/4670b5ff7526319680f4   これら実践や知識がどの程度広く役に立つのか,例えばコンペにおいてどの程度有効なのか,との疑問を持っていたところ,日本語NLPが関連しそうなコンペが開催されていたので参加してみました. コンペ初参加.専門は生物・プラント・医薬系知的財産です. コンペ概要 小説家になろう ブクマ数予測 ~”伸びる”タイトルとは?~ https://www.nishika.com/competitions/21/summary ・与えられたデータは以下の通りです.ブックマーク度を予測します. ・予測精度の評価はMulti-class loglossにて行われます. 要素 説明 ncode Nコード general_firstup 初回掲載日 YYYY-MM-DD HH:MM:SSの形式 title 小説名 story 小説のあらすじ keyword キーワード userid 作者のユーザID(数値) writer 作者名 biggenre 大ジャンル genre ジャンル novel_type 連載の場合は1、短編の場合は2 end 短編小説と完結済小説は0となっています。連載中は1です。 isstop 長期連載停止中なら1、それ以外は0です。 isr15 登録必須キーワードに「R15」が含まれる場合は1、それ以外は0です。 isbl 登録必須キーワードに「ボーイズラブ」が含まれる場合は1、それ以外は0です。 isgl 登録必須キーワードに「ガールズラブ」が含まれる場合は1、それ以外は0です。 iszankoku 登録必須キーワードに「残酷な描写あり」が含まれる場合は1、それ以外は0です。 istensei 登録必須キーワードに「異世界転生」が含まれる場合は1、それ以外は0です。 istenni 登録必須キーワードに「異世界転移」が含まれる場合は1、それ以外は0です。 pc_or_k 1はケータイのみ、2はPCのみ、3はPCとケータイで投稿された作品です。 対象は投稿と次話投稿時のみで、どの端末で執筆されたかを表すものではありません。 fav_novel_cnt_bin ブックマーク度 初見と偏見 ・テーブルデータが主,テキストデータは従となるコンペとなりそう. ・チュートリアルをベースとして検討して良さそう. ・web小説には変化しやすいトレンドが存在するであろう.時系列が重要となるコンペとなりそう? ・NLPを用いることができそうな特徴は,title,keyword,story.後発作は人気作のこれらを真似てくるであろうから,単純な文章類似やヒトが簡単に認知できる単語相関のみからブックマーク度を予測することは難しいかもしれない. ・ヒトが認知し難い部分まで抽出できる,比較的高度な言語モデルを採用する必要があるかもしれない. ・時系列要素を他の特徴と絡める事ができるモデル作りをする必要があるだろう.NLPの結果をテーブルの特徴とすると良いかもしれない.ラグ特徴が重要となるかもしれない. ・チュートリアルではBERTVectorizerを用い文章の768dimベクトルをそのままテーブルの特徴としているが,テーブルデータにはより重要な特徴が多くあることから?,次元削減しておいたほうが良さそうにも思える.比較的高度な言語モデルの必要性を考慮すると,そのまま用いたほうが良いようにも思える. 初期EDA ・tfidf/mbart/mt5/CLIP embeddings cluster visを流用 ・それぞれの点は一つの小説.ブックマーク度が大きいほど暖色となるようにしている. ・tfidf/mbart/mt5/CLIP embeddings cluster visはヒトが簡単に認知できること=ヒトの認知により近いを優先した比較的シンプルなアルゴリズムとしている?が,ある程度のクラスタの存在が確認できる.NLPは予想より有効かもしれない. ・trainは過去データ,testは最新データであるところ,最新データはある程度人気トレンドに沿っているように見える. ・genreごとに分布が全く異なる印象.また,あるジャンルにおいては人気のキーワードが他のジャンルでは不人気であることも.NLPではジャンル分けしたモデル作りも有効かと想定された. ・驚いたことに,(与えられたデータ上では)ほとんどハズレが無いジャンル,というものがあるようだ.genre==101の多くを占めるいわゆる「悪役令嬢もの」がこれに該当する.今後はどうなるかわからないが.(データにはトレンドの隆盛情報はあまりないようであった.現状のトレンドを考慮しすぎるモデルは作れるが,将来に対する汎用性がないモデルとなりそう・・・) ・その他,時系列特性の確認など行ったが省略 結果 ・正確な順位はぼかしますが,次の記事の順位と同等でした.金メダル下位~銀メダル上位です. 【AI Shift Advent Calendar 2021】Nishikaコンペ振り返り 小説家になろう ブクマ数予測 ~”伸びる”タイトルとは?~ https://www.ai-shift.co.jp/techblog/2246 解法 Common Settings Types of networks Model NLP: BERT classification Model NLP2: tfidf/mbart/mt5/CLIP embeddings cluster vis vector Model Table: CatBoost Backbones Model NLP: cl-tohoku/bert-base-japanese-char Model NLP2: self-distributed representation DataSet holdout. trainのうち最も最新に近いデータをvalとした(チュートリアルと同じ) Model NLP: title,keyword,story Model NLP2: title,keyword,story Model Table: All features + original features (keyword word2vec sum dimensionality reduction vector, lag, combination, aggregate, Target Encoding, etc) keyword word2vec sum dimensionality reduction vector: キーワードを分散表現に変更しその要素の和を次元削減し特徴とした Loss Model NLP: weighted loss Optimizer Model NLP: * Augmentation non The track of scores single model Model NLP: LB 0.777 Model Table: LB 0.726 add NLP result and NLP2 dimensionality reduction vector to Table model Model Table: LB 0.685 add genre 101,401 only NLP results to Table model Model Table: LB 0.64* 振り返り 特徴 ・SHAPで特徴の評価を行った.  スコアへの寄与が大きかった特徴,SHAPにおいて評価の大きかった特徴は次の通り ・・genre 101 only NLP results :人気ジャンルほど追従が多く分類に高度な言語モデルが必要であったから? ・・userid_titlelength ・・Model NLP result :意外にBERT特徴の評価も高かった.もう少しやりようがあったかもしれない. ・・userid_isr15_isBL ・・userid ・・・ うまくゆかなかった試行 ・stacking ・tabnet: LB 0.9 ・tfidfCNN: LB 1.3 ・ヒューステリックな後処理 ・分布から重みを計算したBERT weighted loss ・・・ コメント ・コンペ初参加で金メダル下位~銀メダル上位.まずまずの結果ではありました. ・前述した既存の知識が役に立ったか,と言われると?  ・本コンペがNLPコンペであったのかと言われると? ・BERT単独である程度のスコアが得られており,BERTの結果をtableに組み込んだときに最も有効であったことを考慮すれば,NLPにtableの知識構造を導入するコンペであったと言っても良いかもしれません. ・HuggingfaceのBERTをまともに使うのは初めてでしたが,簡単である上に拡張もある程度可能であり非常に楽でした.(そろそろtensorflow1ベースの生BERTから切り替えようかな・・・) ・BERTにおいて,weight lossは必須でした.ただしそのweightは分類の分布比が最適とはなりませんでした. ・genre==101専用のBERTを用いるなどgennre単位で検討してしまいましたが,genreのようなジャンル自体を求めるクラスタリングを行ったほうが,より柔軟なモデルとできたでしょう.深層距離学習が使えたかもしれません. ・ヒトが認知できる程度の単語相関など,いわゆる伝統的なテキストマイニングを用いた任意の特徴づくりを行いましたが,あまりスコアに影響があった感覚がありません.(後発作は有名作のキーワードなどを分析しあらすじなど作成していることを伺わせる記事を見ました.伝統的なテキストマイニングにより得られる程度の情報は,先行作を分析し作成される近年の小説?においては,予測の足しにならなくなっているのかもしれません.BERTなどある程度ブラックボックスであるモデルを用いて初めて,残された情報を探れる状況にあるのかもしれません.そのようないたちごっこなのかもしれません.特許公報でも似た状況になってゆくのかも・・・) ・読みやすさに関する文献等読みそれを考慮した任意の特徴づくりを行いましたが,スコアに影響があった感覚がありません.本文でないあらすじにおける適用では限界があったのかもしれません. ・テーブルデータの取り扱いにつき,学び直しができました.定期的に手を動かしていないとだめですね. ・lag特徴が予想より重要と評価されませんでした.トレンドはほとんど無視しても良かったのかもしれません.StratifiedKFoldを使うべきだったかもしれません. ・テーブルで用いた特徴をBERTに押し押し込む手法もありましたが,ほぼ試していません.トレンドがそれほど重要でなかったのならは試しても面白かったかもしれません. ・EDA重視,特徴生成最重視,モデル検討後回し,のスケジューリングをしました. ・EDAにかける時間は,適当だったと思います.もう少しポイントを絞るべきではありました. ・特徴生成は多々行えましたが,特徴の有効性を細かく検証するシステムを構築しきれませんでした.例えばマイナス効果のある特徴を除去しきれなかったなど問題が残っているかもしれません. ・モデル検討を後回しとした判断は間違っていなかったとは思いますが,最後に回したモデル検討に予想以上に時間を取られました.モデル検討において新たな仮説を思いつくこともありましたので,モデル検討を完全に後回しとするのではなく,モデル検討まで含めたコンパクトなサイクルを作成し,回すべきだったかと思います. ・ブックマーク度の分布はなだらかであったのでわかりませんが,予測対象となるブックマーク度をbinningし回帰的な問題に置換えたほうが良い結果となったかもしれません. ・いくらか未実施の仮説を残したまま期限切れとなってしまいました.  やり残した後悔はありますが,やり残しを実行しても金メダル上位に届いたとは思えません.  振り返り会に参加し,また他参加者の振り返りを見て,再検証することにより,参加時以上の学びがありそうです. 【Nishikaコンペ振り返り会】 小説家になろう ブクマ数予測 ~”伸びる”タイトルとは? https://www.youtube.com/watch?v=IcDHebW-q3E 他参加者の振り返り記事 【AI Shift Advent Calendar 2021】Nishikaコンペ振り返り 小説家になろう ブクマ数予測 ~”伸びる”タイトルとは?~ https://www.ai-shift.co.jp/techblog/2246 なろう小説のブクマ数予測コンペに参加した話 https://qiita.com/pndnism/items/905523333984516f887d ・・・ その他 ・EDA中に小説家になろうサイトの小説検索機能を用い検索してみたのですが,なかなか意中の小説が見つかりませんでした.(キーワード検索では結果に有名作品の追従作が混ざり見分けがたい? useridが重要特徴であったが,これは著者名というブランドに対する将来への期待を示しているとともに,それを用いざるを得ない現行の検索システムの限界も示している気もする.) 例えば以下のような,未知軸を考慮したグラフィカルな検索システムも採用したのなら,より意中の小説を見つけやすくなるのであろうな,と思うところでもありました. *しかしぶっ飛んだ設定の小説があるな・・・
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】magic-classで効率的にGUIを作成する

はじめに Pythonの豊富なライブラリを活かせば、非常に強力なGUIツールを作ることができます。特にデータの可視化においては、1次元データはpyqtgraph、多次元の画像データはnapariがとても優秀で、これらのライブラリを使えるだけでできることが格段に増えます。 pyqtgraphのリポジトリ napariのリポジトリ しかし、GUIを書いたことのある方であれば必ず感じることがあります。 「「さすがにもうちょっと楽にできないのか!」」 これをどうにかできないかと思い、Qtをベースにしたmagic-classというパッケージを開発しています。今回は宣伝もかねてmagic-classの簡単な紹介をします! ※英語ですが今頑張ってドキュメントも書いています。興味があったらこちらも参考にしていただけると嬉しいです。 magic-classの動機 これまでのパッケージの問題点 magic-classで解決したかったのは次の問題点です。 GUIのコードはPythonの読みやすさを全く活かせない。たいてい非常に読みづらくなりメンテナンスが難しい。 「ボタンを押す→引数を指定する→関数を呼ぶ」という超基本な操作ですら、関数ごとにウィジェットを用意する必要がある。 ボタンもメニューの各項目もツールバーの各項目も、やってることほぼ同じなのになぜかメソッド名や設計が異なる。 GUIで手動操作しつつJupyterなどでスクリプトベースで操作する「ハイブリッド」なGUIがPythonの主流になっているにもかかわらず、それを作ろうとするとさらに書くことが増える。 もっと簡潔に悩みを書くとすれば... 「高度なことはできなくてもよいから、シンプルで使いやすいGUIを直観的に作りたい」 ということになります。 magic-classではどうか? magic-classの実装は、次のような設計になっています。 変数の型や型アノテーションを活用してウィジェットを用意していく。スクリプトからの操作と干渉しないように、変数そのものの書き換えは最小限に抑える。 デザインやショートカットなど、付加情報はデコレータにまとめる。 クラス定義のコードの見た目がほぼそのままGUIのレイアウトになるようにする。 特殊文法を導入しない。 APIは可能な限り統一する。 1.~3.は、ウィジェットの細かなデザインでコードが汚れるのを防ぎます。 4.もかなり重要で、これを意識しないと、Web開発で知られるDjangoのように、固有なルールを導入しすぎてPythonを書き慣れた人でも再学習しなければいけないというような状況が生じます。 5.は、前述したように通常のウィジェットとメニューでわずかにメソッドが異なるせいで無駄に検索したりデバッグしたりという問題を防ぎます。 実際にはこんな感じ 詳細は後述するとして、magic-classをつかったコードはこんな感じになります。これは画像をロードし、フィルタをかけ、保存することができるGUIです(関数の内容は省略)。 from magicclass import magicclass, magicmenu, bind_key from magicclass.widgets import Image from pathlib import Path @magicclass # デコレートしたクラスを通常のウィジェットに変換する class A: @magicmenu # デコレートしたクラスをメニューに変換する class Menu: @bind_key("Ctrl-O") # ショートカットを登録する def Open(self, path: Path): """画像を開く""" @bind_key("Ctrl-S") def Save(self, path: Path): """画像を保存する""" image = Image() # 画像表示ウィジェット def apply_filter(self, sigma: float): """画像にフィルタをかけてimageを更新""" これだけでクラスAはGUI用に拡張され、 ui = A() ui.show() により次のGIF画像のようにGUIが起動します。 Path型でアノテーションされたOpenとSaveは、GUIで呼び出されるとファイルダイアログを開きます。apply_filterは浮動小数sigmaの入力が必要なので、それ用のウィンドウが開きます。関数の中身を定義すれば実際にフィルタをかけたりとかできます。 関数自体は書き換えられていないので、 ui.apply_filter(3.0) ui.Menu.Open("/path/to/image.png") のようにスクリプトからの実行もできます。 マクロ記録機能 手動操作から実行可能なスクリプト(マクロ)を生成する機能というのは、Excelをはじめ様々なソフトウェアで用意されています。magic-classではPythonスクリプトを生成するマクロ記録機能が標準搭載されています。次のGIF画像をご覧ください。 コードはこちら from magicclass import magicclass, vfield from magicclass.widgets import QtPlotCanvas @magicclass class A: @magicclass(widget_type="groupbox", layout="horizontal") # ウィジェットのデザインを指定 class Parameters: a = vfield(float, widget_type="FloatSlider", options={"min": 1.0, "max": 10.0, "step": 0.1}) b = vfield(float, widget_type="FloatSlider", options={"min": 1.0, "max": 10.0, "step": 0.1}) plt = QtPlotCanvas() # プロット用のウィジェット def set_title(self, title: str): self.plt.title = title @Parameters.a.connect @Parameters.b.connect def _plot(self): a = self.Parameters.a b = self.Parameters.b x = np.linspace(0, 1, 100) # グラフの更新 self.plt.layers.clear() self.plt.add_curve(np.cos(x*a), np.sin(x*b)) ui = A() ui.show() ui.macro.widget.show() # マクロウィンドウを開く 左のメインウィンドウでの操作が、右のマクロウィンドウにリアルタイムで記録されているのが分かります。マクロ記録機能は汎用性が高いにもかかわらず、実装が非常に大変なので、とても有用な機能だと思います。 使い方 ここからは、magic-classの使い方をもう少し説明していきます。 インストール pipでインストールできます。 pip install magic-class 型とウィジェットの対応 magic-classの型とウィジェットの対応やその実際の実装は、magicguiに従っています。 magicguiリポジトリ magicguiは、magicgui.widgetsにWidgetクラスを継承した様々なウィジェットを用意しており、これらはAPIがとてもよく統一されている素晴らしい設計になっています。例えば、文字列を入力するLineEditも、整数を入力するSpinBoxも、テーブルデータを入力/表示するTableも、値の参照/代入はすべてwidget.value = ...で行われます。 さらに、デコレータ@magicguiを用いて関数をGUI化でき、その際、次の対応表に従って引数の型アノテーションやデフォルト引数の型に対応するWidget型のウィジェットを用意します。 型 ウィジェット ウィジェットの説明 str LineEdit 文字列の入力欄 pathlib.Path FileEdit パスの入力欄と、ファイルダイアログを開くボタン int SpinBox 数値を上下ボタンで調節できる float FloatSpinBox 数値を上下ボタンで調節できる bool CheckBox チェックを入れる/外すことでTrue/Falseを変更 enum.Enum ComboBox 選択肢を選ぶドロップダウンメニュー datetime.datetime DateTimeEdit 日時/時刻を調節できる datetime.date DateEdit 日時を調節できる datetime.time TimeEdit 時刻を調節できる ※他にも、ウィジェット型を指定すればSpinBoxをSliderにしたりもできます。より詳細は公式ドキュメントをご覧ください。 実際に使うと次のようなコードになります。 %gui qt # QtベースのGUIなので、Jupyterではこれを実行する from magicgui import magicgui from datetime import date @magicgui def diary(t: date, text: str): print(f"{t}: {text}") diary.show() magicguiからmagic-classへどう拡張したか magic-classでは、@magicguiを真似て、クラスをデコレートしてGUI化するデコレータをいくつか用意しています。 @magicclass → メインウィンドウとなる通常のウィジェット @magicmenu → メニュー @magiccontext → 右クリックで現れるコンテキストメニュー @magictoolbar → ツールバー これらでデコレートするクラスの書き方に違いはありません。 さらに、@magicclassに関しては、引数widget_type=...を与えることで、スクロールバー付きだったり、タブに分けたりしたウィジェットに切り替えることができます。もちろんクラスの書き方に変更はありません。詳細はドキュメントにあります。 magic-classがクラスをウィジェットに変換する際の要点は (1) クラスのメソッド一つ一つを@magicguiでデコレートして、それを呼び出すボタン(またはメニュー)を追加する (2) magicgui.widgetsにあるWidgetオブジェクトはそのまま追加する (3) クラス内に他のmagic-classが現れた場合、再帰的にウィジェットを作成してから追加する の3点です。さらに、 (2') 型からウィジェットに変換する関数vfieldを用意する ことで、すべてを型→ウィジェットの変換で済ませることができました。vfieldはPythonの標準ライブラリdataclassesのFieldを継承したオブジェクトを返すもので、dataclasses.fieldと使い方が非常に似ています。 a = vfield(int) # intなので、GUIではSpinBoxに変換 a = vfield(1) # 1はintなので、1にセットされたSpinBoxに変換 のように使います。 簡単な例 電卓を実装した例です。 from magicclass import magicclass, vfield @magicclass class A: a = vfield(int) b = vfield(int) def add(self): self.result = str(self.a + self.b) def sub(self): self.result = str(self.a - self.b) def mul(self): self.result = str(self.a * self.b) def div(self): self.result = str(self.a / self.b) result = vfield(str) クラスAの変数a, b, add, sub, mul, div, resultが順にウィジェットに変換されていることが分かると思います。 おわりに GUIはいくつかのライブラリを試しましたが、magicguiの理念は斬新で比類ないものだと思っています。magicguiの足りない部分をmagic-classでカバーすることで、GUI開発がかなり捗るようになりました。 ぜひ使っていただけると嬉しいです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【番外編】conda list を pip freeze のような出力形式にする

はじめに Python で Web アプリをデプロイする際に、 Django や Flask で Web アプリ開発を行い Heroku にデプロイしたりすることが多いと思います。 このとき、conda でライブラリのインストールをしていることがあると思います。 デプロイのため、requirements.txt にライブラリを記載することになりますが、 pip の場合は、 pip freeze > requirements.txt で簡単にインストール済みのライブラリ一覧を作成できます。 conda の場合は、 conda list -e > requirements.txt とすると、ライブラリの一覧は作成できますが、{ライブラリ名}=={バージョン}の形式に毎回変更しなければならず、少し面倒です。 bash であれば awk や sed コマンドが使えるので、正規表現と組み合わせて、 pip freeze の出力形式に近い形で出力できるようにしてみます。 awk, sed を利用した conda ライブラリ一覧の出力 conda list -e | awk -F'=' '{print $1,"==",$2}' | sed 's/ //g' | sed '/^#/d' > requirements.txt このコマンドは、bash, zsh などで動作します。 powershell では動作しないので気をつけてください。 おおかた pip freeze と同じような出力形式で conda のライブラリ一覧を出力できると思いますので、requirements.txt の作成が楽になると思います。 さいごに 上記のコマンドよりも良いやり方はあると思います。 正規表現は、さまざまな場面で使えそうだと思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Python】conda list を pip freeze のような出力形式にする

はじめに Python で Web アプリをデプロイする際に、 Django や Flask で Web アプリ開発を行い Heroku にデプロイしたりすることが多いと思います。 このとき、conda でライブラリのインストールをしていることがあると思います。 デプロイのため、requirements.txt にライブラリを記載することになりますが、 pip の場合は、 pip freeze > requirements.txt で簡単にインストール済みのライブラリ一覧を作成できます。 conda の場合は、 conda list -e > requirements.txt とすると、ライブラリの一覧は作成できますが、{ライブラリ名}=={バージョン}の形式に毎回変更しなければならず、少し面倒です。 bash であれば awk や sed コマンドが使えるので、正規表現と組み合わせて、 pip freeze の出力形式に近い形で出力できるようにしてみます。 awk, sed を利用した conda ライブラリ一覧の出力 conda list -e | awk -F'=' '{print $1,"==",$2}' | sed 's/ //g' | sed '/^#/d' > requirements.txt このコマンドは、bash, zsh などで動作します。 powershell では動作しないので気をつけてください。 おおかた pip freeze と同じような出力形式で conda のライブラリ一覧を出力できると思いますので、requirements.txt の作成が楽になると思います。 さいごに 上記のコマンドよりも良いやり方はあると思います。 正規表現は、さまざまな場面で使えそうだと思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Griffin-Lim アルゴリズムとは?

はじめに tacotronを学んでいくうちに出会ったGriffin-Limアルゴリズムについての備忘録 短時間フーリエ変換(stft) Griffin-Limアルゴリズムを学ぶ前に短時間フーリエ変換の知識が必要になるので解説します。(自分はこんなものかと考えているため間違っていたら教えてください・・) フーリエ変換は波形に対してどの周波数成分がどのくらい含まれているかを分析する手法です。 波形がどのような三角関数で表されているのかがわかります。つまりどんな波形も三角関数を足し合わせれば再現できます。 短時間フーリエ変換は波形に対してフーリエ変換の範囲と移動幅を決め短い区間でフーリエ変換を移動幅の分だけ繰り返す手法です。(うまく伝わるかな?) 時間的に変化する周波数成分を捉えられることがメリットです。 例えば、歌にフーリエ変換を用いても今までのメロディーとは全く異なるcパートが入るとそれも合わせてフーリエ変換してしまいます。 短時間フーリエ変換を上記の例に施してあげれば時間的に変化する周波数成分(aパートやbバートやcパート、移動幅を小さくすればパートの更に細かいところ)を捉えられます。 etc 短時間フーリエ変換は、移動幅を小さくしすぎると時間成分はいいもの周波数成分が悪くなってしまい、移動幅を大きくしすぎると今度は周波数成分はいいものの時間成分が悪くなってしまいます。これを不確定性原理といいます。 短時間フーリエ変換の実装 実装ではpytorchを使っていきます。pytorchのインストール方法についてはここで書くと長くなってしまうので省略します。 pytorchでは短時間フーリエ変換を1行で実装できます。 以下の形でpytorchの短時間フーリエ変換が提供されています。 以下実装です。 def STFT(data, n_fft): stft = torch.stft( input=data, n_fft=n_fft ) return stft torch.stft()では移動幅はデフォルトだと短時間フーリエ変換をしてあげる幅の1/4に設定されています。 戻り値の形はTensor型です。 逆短時間フーリエ変換(istft) 短時間フーリエ変換には逆変換があります。それが逆短時間フーリエ変換です。 逆短時間フーリエ変換の実装 pytorchでは逆短時間フーリエ変換について以下の形で提供しています。 これを用いて実装します def ISTFT(data, n_fft): istft = torch.istft( input=data, n_fft=n_fft ) return istft これで実装ができました。 これだけだと本当に逆変換ができたのか分かりづらいので短時間フーリエ変換前と短時間フーリエ変換後の波形を見てみましょう。 filepath = 'data/audio.wav' audiodata ,sr = torchaudio.load(filepath) #入力されたデータの波形の確認 fig = plt.figure() a1 = fig.add_subplot(211) a1 = plt.plot(audiodata.t().numpy()) if __name__ == '__main__': #短期間フーリエ変換を施してあげる幅はとりあえず400にする stft = STFT(audiodata,400) istft = ISTFT(stft,400) #逆変換されたデータの波形の確認 a2 = fig.add_subplot(212) a2 = plt.plot(istft.t().numpy()) plt.show() 自分はLJSpeechの適当な音声のデータを入れてあげました。 結果は以下です。 上は、短時間フーリエ変換を施してあげる前 下は、逆短時間フーリエ変換を施してあげた後 ほとんど同じになりました。しっかり逆変換できて元の波形に戻っているようです。 Griffin-Limアルゴリズムとは? 前置きが長くなりましたが本題です。 はじめにで少し述べたTTSモデルのtacotronはテキストから振幅スペクトログラムを予測します。予測した振幅スペクトログラムを逆短時間フーリエ変換で音声にしたい場合、位相の情報が足りません。そこで位相を予測するためのアルゴリズムとしてGriffin-Limアルゴリズムが登場します。 Griffin-Limアルゴリズムは振幅スペクトログラムから位相をランダム(乱数を用いて)に初期化します。その後は以下の3つのステップを繰り返します。 (1)逆短時間フーリエ変換で振幅スペクトログラムとランダムに生成した位相スペクトログラムから波形を生成。 (2)その波形に短時間フーリエ変換を施す。 (3)(2)で得た振幅スペクトログラムを元の振幅スペクトログラムに置き換える。 これを繰り返すことでスペクトログラムの無矛盾性から位相が得られます。 十分に繰り返すと品質の良い結果が得られます。 実装 pytorchではGriffin-Limアルゴリズムについて以下の形で提供しています。 これを用いて実装します。 def GL(audiodata,sr,n_fft,n_iter): #torchaudio.transforms.GriffinLimのデフォルトに沿ったパラメータ win_length=n_fft hop_length=win_length // 2 #Griffin_Limアルゴリズムの入力となるスペクトログラムの作成 spec = torchaudio.transforms.Spectrogram( n_fft=n_fft, win_length=win_length, hop_length=hop_length, )(audiodata) #Griffin-Limアルゴリズム本体 griffin_lim = torchaudio.transforms.GriffinLim( n_fft=n_fft, #n_iterはGriffin-Limの3つのステップ反復回数、デフォルトは32 n_iter=n_iter, win_length=win_length, hop_length=hop_length, ) outaudio = griffin_lim(spec) return outaudio これで実装できました。実際に動かしてみましょう。 filepath = 'data/audio.wav' audiodata ,sr = torchaudio.load(filepath) #入力されたデータの波形の確認 fig = plt.figure() a1 = fig.add_subplot(211) a1 = plt.plot(audiodata.t().numpy()) if __name__ == '__main__': #反復回数はデフォルトの32でいれてみる gl = GL(audiodata,sr,400,32) #Griffin-Limアルゴリズムでスペクトルから復元されたデータの波形の確認 a2 = fig.add_subplot(212) a2 = plt.plot(gl.t().numpy()) plt.show() 動かしてみた結果が以下です。 上が元の音声波形で下がGriffin-Limアルゴリズムで復元した波形です。 ちょっと違うのがわかるでしょうか? 反復回数を10倍にしてみた結果は以下です。 10倍にする前よりはよくなった・・・のかな? Griffin-Limアルゴリズムの現在 ここまで話したら、tacotronでGriffin-Limアルゴリズムを使っているのだろうとお思いでしょうがそうではありません。 Griffin-Limアルゴリズムの紹介で「十分に繰り返すと品質の良い結果が得られます」と記載しましたがそれは反復回数ごとの話です。 十分に反復回数を増やしたところで実際に聞いてみるとあまり良くない結果が返ってくることもあります。 現在、スペクトログラムから音声の変換ではwavenetをはじめとするニューラルボコーダが品質が良く、tacotronでもニューラルボコーダがスペクトログラムから音声波形の変換に用いられています。 実用的にこのGriffin-Limアルゴリズムは品質が問題視されないところで用いられているのが現状です。 おわりに ニューラルボコーダを学んでいるうちにであったGrifin-Limアルゴリズムについてまとめてみた。 おもいつきでまとめてみたため勘違いもいっぱいありそう。(いつも知識の勘違いで教授に怒られている・・) 初心者であるため間違いが多少あるかもしれません。もし、間違えているところがありましたらこっそり教えてください。 この記事が誰かのお役に立てれば幸いです。 参考 短時間、逆短時間フーリエ変換、メルスペクトログラム、Griffin-Limアルゴリズムについては以下を参考にしました。特に「pythonで学ぶ音声合成」は音声を学ぶ上ですごくお世話になっています。音を学ぶ際にはぜひ読みたい一冊だと思います。興味があればぜひ。 山本 龍一、 高道 慎之介.pythonで学ぶ音声合成.インプレス.2021 https://book.impress.co.jp/books/1120101073 高道 慎之介、齋藤 佑樹、高宗 典玄、北村 大地、猿渡 洋.von Mises分布DNNに基づく振幅スペクトログラムからの位相復元.情報処理学会研究報告.2012 http://sython.org/papers/SIG-SLP/takamichi1806slp_paper.pdf
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【自分用メモ】Pythonのコメント機能を使って簡単にコードを切り替える

忘れていたのでメモします。 参考になれば幸いです。 コード #""" #ここのコメントアウトを解除する VAR = 1 """ VAR = 0 #""" VSCodeでの様子
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pythonのコメント機能を使って簡単にコードを切り替える

発見しました。 参考になれば幸いです。 コード #""" #ここのコメントアウトを解除する VAR = 1 """ VAR = 0 #""" VSCodeでの様子
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

③PythonAnywhere Google Newsのheadlineをスクレイピングしてメールで毎朝定期的に送る

クラウド上のサーバーでPythonのコードを実行できるPythonAnywhereで無料のアカウントでもメールを送ったり、スクレイピングすることを説明しました。 ①PythonAnywhereの無料アカウントからメールを送る https://qiita.com/Kent-747/items/fa92c51d81cd4f50eadc ➁PythonAnywhereの無料アカウントでGoogle Newsをスクレイピング https://qiita.com/Kent-747/items/0380de9ec7d6de137afa 今回は、それらを組み合わせて、Google Newsのヘッドラインをスクレイピングした結果をメールで送ってみたいと思います。 基本的には、以前紹介したコードを組み合わせただけです。 import requests from bs4 import BeautifulSoup import re import smtplib, ssl from email.mime.text import MIMEText # SMTP認証情報 account = "xxxxx@gmail.com" password = "yyyyy" # 送受信先 to_email = "zzzzz@gmail.com" from_email = "xxxxx@gmail.com" ######## ここからGooglニュースのトップページ情報を取得する ##### URL = "https://news.google.com/topstories?hl=ja&gl=JP&ceid=JP:ja" rest = requests.get(URL) # BeautifulSoupにGoogleニュースのページ内容を読み込ませる soup = BeautifulSoup(rest.text, "html.parser") # Googleニュースの見出しとURLの情報を取得して出力する data_list = soup.find_all('h3') allHeadlines ='' for data in data_list: print(data.text) allHeadlines += data.text allHeadlines += "<br>" # MIMEの作成 subject = "本日のGoogle News" msg = MIMEText(allHeadlines, "html") msg["Subject"] = subject msg["To"] = to_email msg["From"] = from_email server = smtplib.SMTP_SSL("smtp.gmail.com", 465, context=ssl.create_default_context()) server.login(account, password) server.send_message(msg) server.quit() 今回、少しはまったところは、ヘッドラインをメッセージとして準備する際にで改行させて連結するところです。 こちらを保存してPythonAnywhere上で実行することは前回の記事を参考にしてください。 それが動くことが確認できたら、次のようにTaskとして毎朝メールが発信できるようすることもできます UTCでしか時間は設定できなくて、日本時間から+9hとのことです。 私は毎朝7:35に届くようにしてみました。 このように無事、届きました!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

matplotlib 3次元グラフ(アスペクト比調整と多角形塗りつぶし)

はじめに 格子桁解析を行っているのだが、結果の断面力図は3次元で表示する必要がある。これまで3次元描画はGMT(Generic Mapping Tools)を使っていたのだが、なんとかPythonでやってみたい。 Pythonで3次元描画を行う上での問題点は、「各軸の縮尺(アスペクト比)を合わせること」および、「多角形(ポリゴン)の着色」であったが、一応これらを思ったとおりに実行できるようになったので、基本的なところを紹介しておく。なお、この原稿の下書きは、最近使い出したメモアプリ(マークダウンエディタ) Obsidian で行っている。なかなかいいですぞ。 環境は以下の通り。 macOS Monterey Python 3.9.7 matplotlib 3.4.3 参考サイトを以下に示しておく。 アスペクト比関係 3Dポリゴン関係(1) 3Dポリゴン関係(2) デフォルト描画 デフォルトだと背景やアスペクト比が自動で設定されてしまい、ゴタゴタすぎる感がある。 以下に作図プログラムを示す。 import matplotlib.pyplot as plt fig = plt.figure(figsize=(6,6)) ax = fig.add_subplot(111, projection='3d') xmin,xmax=-2,20 ymin,ymax=-3,12 zmin,zmax=-7,15 ax.set_xlim3d(xmin, xmax) ax.set_ylim3d(ymin, ymax) ax.set_zlim3d(zmin, zmax) ax.set_xlabel('x-axis') ax.set_ylabel('y-axis') ax.set_zlabel('z-axis') # plot bullets ax.plot([5,10],[0,0],[0,0],'o') ax.plot([0,0],[5,10],[0,0],'o') ax.plot([0,0],[0,0],[5,10],'o') # plot square shape ax.plot([2,6,6,2,2],[2,2,6,6,2],[0,0,0,0,0],'-') ax.plot([0,0,0,0,0],[2,6,6,2,2],[2,2,6,6,2],'-') ax.plot([12,16,16,12,12],[0,0,0,0,0],[2,2,6,6,2],'-') plt.savefig('fig_1.jpg',dpi=100) plt.show() アスペクト比をあわせる・視点を変える ここでは、以下を実行。 (1) 視点を変更する。 (2) デフォルトの軸・背景はとってしまう。 (3) x,y,z軸の縮尺を合わせる(アスペクト比を調整)。 (1) ax.view_init(elev=30, azim=-135) (2) plt.axis('off') (3) ax.set_box_aspect((xmax-xmin,ymax-ymin,zmax-zmin)) 視点は、elev :z軸を見下げる角度、azim :x-y平面の回転角で指定する。 アスペクト比の調整はax.set_box_aspect((1,1,1)) とするようなものをよく目にするが、x,y,z軸の長さが等しいときにしか使えないため、ここではそれぞれの軸の長さを指定している。 以下に作図プログラムを示す。3つの正方形の座標を与えているが、一応正方形になっているようである。 import matplotlib.pyplot as plt xmin,xmax=-2,20 ymin,ymax=-3,12 zmin,zmax=-7,15 fsz=10 fig = plt.figure(figsize=(6,6)) ax = fig.add_subplot(111, projection='3d') ax.view_init(elev=30, azim=-135) plt.axis('off') ax.set_box_aspect((xmax-xmin,ymax-ymin,zmax-zmin)) ax.set_xlim3d(xmin, xmax) ax.set_ylim3d(ymin, ymax) ax.set_zlim3d(zmin, zmax) # draw axes ax.plot([xmin,xmax],[0,0],[0,0],'-',color='#000000',lw=1) ax.plot([0,0],[ymin,ymax],[0,0],'-',color='#000000',lw=1) ax.plot([0,0],[0,0],[zmin,zmax],'-',color='#000000',lw=1) ax.text(xmax,0,0,'x',fontsize=fsz) ax.text(0,ymax,0,'y',fontsize=fsz) ax.text(0,0,zmax,'z',fontsize=fsz) # plot bullets ax.plot([5,10],[0,0],[0,0],'o') ax.plot([0,0],[5,10],[0,0],'o') ax.plot([0,0],[0,0],[5,10],'o') # plot square shape ax.plot([2,6,6,2,2],[2,2,6,6,2],[0,0,0,0,0],'-') ax.plot([0,0,0,0,0],[2,6,6,2,2],[2,2,6,6,2],'-') ax.plot([12,16,16,12,12],[0,0,0,0,0],[2,2,6,6,2],'-') plt.savefig('fig_2.jpg',dpi=100) plt.show() 塗りつぶし四角形・文字を加える 多角形の塗りつぶしを行うには以下をインポート。 from mpl_toolkits.mplot3d.art3d import Poly3DCollection プロット及び塗りつぶしは以下の要領で行う。 xx=np.array([0,5,5,0,0]) yy=np.array([0,0,5,5,0]) zz=np.array([10,10,10,10,10]) verts = [list(zip(xx,yy,zz))] ax.add_collection3d(Poly3DCollection(verts,facecolor='#000080',alpha=0.3)) 点の座標指定はリストでも問題ないが、描画軸の入れ替えを行う際などで符号を付ける際の扱いがnumpy配列のほうが便利なので、numpy配列にしている。verts を指定するリストは zip で囲うところがミソらしい。色および透明度の指定は、Poly3DCollection() の中で行うことができる。 文字列の描画は3次元座標でも指定できるが、ここでは、グラフ領域を(0,0)~(1,1) で表す座標を用いて、以下のように文字を描画している。すなわち、グラフ領域の左上に、上詰め・左詰めで文字列 ls を配置している。 ax.text2D(0, 1, ls, va='top',ha='left',transform=ax.transAxes,fontsize=fsz) 作図用プログラムを以下に示す。 import matplotlib.pyplot as plt from mpl_toolkits.mplot3d.art3d import Poly3DCollection import numpy as np xmin,xmax=-2,20 ymin,ymax=-3,12 zmin,zmax=-7,15 fsz=10 fig = plt.figure(figsize=(6,6)) plt.rcParams['font.family']='monospace' ax = fig.add_subplot(111, projection='3d') ax.view_init(elev=30, azim=-135) plt.axis('off') ax.set_box_aspect((xmax-xmin,ymax-ymin,zmax-zmin)) #ax.set_box_aspect((1,1,1)) ax.set_xlim3d(xmin, xmax) ax.set_ylim3d(ymin, ymax) ax.set_zlim3d(zmin, zmax) # draw axes ax.plot([xmin,xmax],[0,0],[0,0],'-',color='#000000',lw=1) ax.plot([0,0],[ymin,ymax],[0,0],'-',color='#000000',lw=1) ax.plot([0,0],[0,0],[zmin,zmax],'-',color='#000000',lw=1) ax.text(xmax,0,0,'x',fontsize=fsz) ax.text(0,ymax,0,'y',fontsize=fsz) ax.text(0,0,zmax,'z',fontsize=fsz) # plot bullets ax.plot([5,10],[0,0],[0,0],'o') ax.plot([0,0],[5,10],[0,0],'o') ax.plot([0,0],[0,0],[5,10],'o') # plot square shape ax.plot([2,6,6,2,2],[2,2,6,6,2],[0,0,0,0,0],'-') ax.plot([0,0,0,0,0],[2,6,6,2,2],[2,2,6,6,2],'-') ax.plot([12,16,16,12,12],[0,0,0,0,0],[2,2,6,6,2],'-') # colored polygon xx=np.array([0,5,5,0,0]) yy=np.array([0,0,5,5,0]) zz=np.array([10,10,10,10,10]) verts = [list(zip(xx,yy,zz))] ax.add_collection3d(Poly3DCollection(verts,facecolor='#000080',alpha=0.3)) ls1='ax.view_init(elev=30, azim=-135)' ls2='xmin,xmax=-2,20' ls3='ymin,ymax=-3,12' ls4='zmin,zmax=-7,15' ls=ls1+'\n'+ls2+'\n'+ls3+'\n'+ls4 ax.text2D(0, 1, ls, va='top',ha='left',transform=ax.transAxes,fontsize=fsz) plt.savefig('fig_3.jpg',dpi=100) plt.show() 作品例 ここで紹介した手法を用いて格子桁解析結果を示す断面力図を作成した事例を以下に示す。これの作図用コードは、格子桁解析の結果読み込みから始まり冗長なので省略する。こんな図も作れるんだ程度に見てほしい。もっと見やすくできないかについては更に経験値を積むことにするが、まずまずの出来か?(自画自賛) 以 上
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Project Euler】Problem 15: 格子状の道

本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 15. 格子状の道 原文 Problem 15: Lattice paths 問題の要約:20 x 20の格子状の道を左上から右下まで行くルートは何通りあるか求めよ 各格子点に入る道は、上からと左からの2つで、それぞれの前の格子点までのルートの数を足せばよいので、パスカルの三角形(Wikipedia) になることが分かります。パスカルの三角形は前の行を一つずらして足して行きます。n=2の時は4行目まで求めて、その真ん中の値が答えになっています。 import numpy as np n = 2 p = np.array([1]) for i in range(2*n): p = np.append(p, 0)+np.append(0, p) print(p) print(f"Answer : {p[n]}") #[1 1] #[1 2 1] #[1 3 3 1] #[1 4 6 4 1] #Answer : 6 もちろんパスカル三角形のn行r列は組合せの数$nCr$なので、sympyのbinomialでも簡単に求めることが出来ます。 from sympy import binomial n = 20 print(binomial(2*n, n)) (開発環境:Google Colab)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Project Euler】Problem 14: 最長のコラッツ数列

本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 14. 最長のコラッツ数列 原文 Problem 14: Longest Collatz sequence 問題の要約: $N<10^6$ で最長のコラッツ数列となる開始の数を求めよ 昨年朝日新聞にも「「コラッツ予想」証明できたら1億2000万円」で紹介されたコラッツ数列の問題ですね。定義はコラッツの問題(Wikipedia)に詳細があります。問題そのものは定義に沿って書けば簡単で、数列の長さを求める関数collatzLenは以下のようになります。 import itertools def collatzLen(n): for cnt in itertools.count(2): n = n // 2 if n % 2 == 0 else 3*n + 1 if n == 1: break return cnt termmax = 0 N = 10**6 for n in range(2, N): term = collatzLen(n) if term > termmax: startn, termmax = n, term print(f"Answer : {termmax} at n={startn}") これだけだと面白くないのでWikipediaにもあるグラフをmatplotlibで書いてみました。x軸がnで、y軸が数列の長さです。1つだけ飛び出ているのが答えですね。プログラムは下にあります。 import matplotlib.pyplot as plt N = 10**6 fig = plt.figure(figsize=(12,12)) ax = fig.add_subplot(1, 1, 1) ax.set_xlim(0, N) ax.set_ylim(0, 600) ax.grid() for n in range(1,N+1,2): cs = collatzLen(n) ax.plot(n, cs, marker='.',markersize= 3, color='b') plt.show (開発環境:Google Colab)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Project Euler】Problem 13: 大きな数の合計

本記事はProjectEulerの「100番以下の問題の説明は記載可能」という規定に基づいて回答のヒントが書かれていますので、自分である程度考えてみてから読まれることをお勧めします。 問題 13.大きな数の合計 原文 Problem 13: Large sum 問題の要約:100個の50桁の数の合計の最初の10桁を求めよ これもPythonのように桁数制限がないと簡単ですね。 print(f"Answer : {str(sum((list(map(int,list(ln.split()))))))[:10]}") ln = "37107287533902102798797998220837590246510135740250 "\ "46376937677490009712648124896970078050417018260538 "\ "74324986199524741059474233309513058123726617309629 "\ "91942213363574161572522430563301811072406154908250 "\ "23067588207539346171171980310421047513778063246676 "\ "89261670696623633820136378418383684178734361726757 "\ "28112879812849979408065481931592621691275889832738 "\ "44274228917432520321923589422876796487670272189318 "\ "47451445736001306439091167216856844588711603153276 "\ "70386486105843025439939619828917593665686757934951 "\ "62176457141856560629502157223196586755079324193331 "\ "64906352462741904929101432445813822663347944758178 "\ "92575867718337217661963751590579239728245598838407 "\ "58203565325359399008402633568948830189458628227828 "\ "80181199384826282014278194139940567587151170094390 "\ "35398664372827112653829987240784473053190104293586 "\ "86515506006295864861532075273371959191420517255829 "\ "71693888707715466499115593487603532921714970056938 "\ "54370070576826684624621495650076471787294438377604 "\ "53282654108756828443191190634694037855217779295145 "\ "36123272525000296071075082563815656710885258350721 "\ "45876576172410976447339110607218265236877223636045 "\ "17423706905851860660448207621209813287860733969412 "\ "81142660418086830619328460811191061556940512689692 "\ "51934325451728388641918047049293215058642563049483 "\ "62467221648435076201727918039944693004732956340691 "\ "15732444386908125794514089057706229429197107928209 "\ "55037687525678773091862540744969844508330393682126 "\ "18336384825330154686196124348767681297534375946515 "\ "80386287592878490201521685554828717201219257766954 "\ "78182833757993103614740356856449095527097864797581 "\ "16726320100436897842553539920931837441497806860984 "\ "48403098129077791799088218795327364475675590848030 "\ "87086987551392711854517078544161852424320693150332 "\ "59959406895756536782107074926966537676326235447210 "\ "69793950679652694742597709739166693763042633987085 "\ "41052684708299085211399427365734116182760315001271 "\ "65378607361501080857009149939512557028198746004375 "\ "35829035317434717326932123578154982629742552737307 "\ "94953759765105305946966067683156574377167401875275 "\ "88902802571733229619176668713819931811048770190271 "\ "25267680276078003013678680992525463401061632866526 "\ "36270218540497705585629946580636237993140746255962 "\ "24074486908231174977792365466257246923322810917141 "\ "91430288197103288597806669760892938638285025333403 "\ "34413065578016127815921815005561868836468420090470 "\ "23053081172816430487623791969842487255036638784583 "\ "11487696932154902810424020138335124462181441773470 "\ "63783299490636259666498587618221225225512486764533 "\ "67720186971698544312419572409913959008952310058822 "\ "95548255300263520781532296796249481641953868218774 "\ "76085327132285723110424803456124867697064507995236 "\ "37774242535411291684276865538926205024910326572967 "\ "23701913275725675285653248258265463092207058596522 "\ "29798860272258331913126375147341994889534765745501 "\ "18495701454879288984856827726077713721403798879715 "\ "38298203783031473527721580348144513491373226651381 "\ "34829543829199918180278916522431027392251122869539 "\ "40957953066405232632538044100059654939159879593635 "\ "29746152185502371307642255121183693803580388584903 "\ "41698116222072977186158236678424689157993532961922 "\ "62467957194401269043877107275048102390895523597457 "\ "23189706772547915061505504953922979530901129967519 "\ "86188088225875314529584099251203829009407770775672 "\ "11306739708304724483816533873502340845647058077308 "\ "82959174767140363198008187129011875491310547126581 "\ "97623331044818386269515456334926366572897563400500 "\ "42846280183517070527831839425882145521227251250327 "\ "55121603546981200581762165212827652751691296897789 "\ "32238195734329339946437501907836945765883352399886 "\ "75506164965184775180738168837861091527357929701337 "\ "62177842752192623401942399639168044983993173312731 "\ "32924185707147349566916674687634660915035914677504 "\ "99518671430235219628894890102423325116913619626622 "\ "73267460800591547471830798392868535206946944540724 "\ "76841822524674417161514036427982273348055556214818 "\ "97142617910342598647204516893989422179826088076852 "\ "87783646182799346313767754307809363333018982642090 "\ "10848802521674670883215120185883543223812876952786 "\ "71329612474782464538636993009049310363619763878039 "\ "62184073572399794223406235393808339651327408011116 "\ "66627891981488087797941876876144230030984490851411 "\ "60661826293682836764744779239180335110989069790714 "\ "85786944089552990653640447425576083659976645795096 "\ "66024396409905389607120198219976047599490197230297 "\ "64913982680032973156037120041377903785566085089252 "\ "16730939319872750275468906903707539413042652315011 "\ "94809377245048795150954100921645863754710598436791 "\ "78639167021187492431995700641917969777599028300699 "\ "15368713711936614952811305876380278410754449733078 "\ "40789923115535562561142322423255033685442488917353 "\ "44889911501440648020369068063960672322193204149535 "\ "41503128880339536053299340368006977710650566631954 "\ "81234880673210146739058568557934581403627822703280 "\ "82616570773948327592232845941706525094512325230608 "\ "22918802058777319719839450180888072429661980811197 "\ "77158542502016545090413245809786882778948721859617 "\ "72107838435069186155435662884062257473692284509516 "\ "20849603980134001723930671666823555245252804609722 "\ "53503534226472524250874054075591789781264330331690" (開発環境:Google Colab)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む