情報学部大学生のダラダラ日記

β日記

機械学習や日記っぽいものを書きます

VAEを理論的に理解する

はじめに

今回は、VAE(Variational AutoEncoder)について数式面のみのアウトプットをしたいと思います。VAEとはなんたるか、実装方法等には触れません。そこではじめに参考文献を示します。

参考文献

Variational Autoencoder徹底解説 - Qiita

AutoEncoder, VAE, CVAEの比較 〜なぜVAEは連続的な画像を生成できるのか?〜 - Qiita

オートエンコーダ:抽象的な特徴を自己学習するディープラーニングの人気者 - DeepAge

猫でも分かるVariational AutoEncoder

イェンセン(Jensen)の不等式の直感的理解 - Qiita

正規分布間のKLダイバージェンス - Qiita

本題

初めに目的を明確にしましょう。下に簡単なVAEのモデルを示します。

 

f:id:Parco1021:20200223170120p:plain

 

{\displaystyle EncoderへXを入力します。そして潜在変数zのためのパラメータμおよびσを出力します。}

{\displaystyle そしてzをDecoderへ入力することで本来求めたい\hat{X} を出力します。}Encoderで直接潜在変数{\displaystyle z}を生成するのではなく、{\displaystyle zのためのパラメータμとσ}を生成することが重要です。

ここで推定したいモデルはDecoder側の{\displaystyle p(X)}ですね。この{\displaystyle p(X)}の尤度(尤もらしさ)を最大にするパラメータ{\displaystyle θおよびϕ}最尤推定法を用いて求めます。

また、色々都合がいいので対数尤度{\displaystyle logp(X)}を最大にします。

f:id:Parco1021:20200223175638p:plain

1行目は確率の加法定理を用いています。同時分布{\displaystyle p(X,z)においてz}について加算すると{\displaystyle X}の分布になるということですね。
2行目は{\displaystyle \frac{q(z|X)}{q(z|X)}=1}を掛けているだけです。

3行目はイェンセンの不等式を使いました。上に凸な関数{\displaystyle f(x)}において{\displaystyle f(E[x])≥E[f(x)]}が成り立つといったものです。

4行目は変分下限を新しく定義しました。変分下限はELBO(evidence lower bound)とも呼びます。複雑な確率分布において周辺尤度は厳密には計算することができません。よって周辺尤度の下限{\displaystyle L(X,z)}をなるべく大きくすることで{\displaystyle logp(X)}へ近づけます。下からすくっていくイメージですね。

 

では対数尤度と変分下限の差は何を示すのかを確認しましょう。

f:id:Parco1021:20200223190656p:plain

{\displaystyle KL[]}KLダイバージェンスと言います。簡単に言うと両者の距離を表すものですが絶対値は取らないので厳密な距離ではないことには注意してください。

2行目の第1項は{\displaystyle \int q(z|X)dz=1}を掛けているだけです。

3行目の第1項は{\displaystyle p(X)}{\displaystyle z}に依存しないため積分記号の内へ入れています。第2項は乗法定理から{\displaystyle p(X,z)=p(z|X)p(X)}を代入しています。

4行目は{\displaystyle log}を分解しています。そしてこれから第1項が消えることがわかります。

6行目は対数をまとめています。

 

ここでもう一度変分下限について整理してみます。

f:id:Parco1021:20200223211235p:plain

{\displaystyle logp(X)}は固定値であるため目的とする"L(X,z)を最大化すること"は'第2項を最小化すること'と同義となりました。

では第2項に着目してみましょう。

f:id:Parco1021:20200225214501p:plain

最小化したい第2項がさらに3つの項へ分解されました。

2行目はベイズの定理{\displaystyle p(X|Y)=\frac{p(Y|X)p(X)}{p(Y)}}を代入し、対数の法則から分解しました。

3行目はまず{\displaystyle p(X)}{\displaystyle q_φ(z|X)}に依存しないため期待値の外へ出します。そして今後のために{\displaystyle logp_θ(X|z)}を別で記述します。

3行目の第1項がKLダイバージェンスの記述ができるので4行目において置き換えています。

これを最小化したいのですが少しよくわからないので変分下限の式に代入してみましょう。

f:id:Parco1021:20200225214731p:plain

代入することで{\displaystyle logp(X)}が消えました。この式を最大化(変分下限は最大化、先ほどのKLダイバージェンスは最小化)したいのでしたね。そのために第1項を小さくし、第2項を大きくしたいということです。それでは各項に注目します。

第1項

第1項は正則化項(Regularization Parameter)です。KLダイバージェンスの形をしているので当然{\displaystyle p(z)}{\displaystyle q(z|X)}の分布が近ければ近いほどこの値は小さくなります。

f:id:Parco1021:20200225215521p:plain

 

2行目~3行目の導出は正規分布のKLダイバージェンスの導出となり複雑なので理解しなくていいと思います。一応リンク載せます。

qiita.com

 

第2項

f:id:Parco1021:20200225212223p:plain

まずは直感的に理解してみましょう。{\displaystyle q(z|X)}はencoder部分で{\displaystyle p(X|z)}はdecoder部分です。つまりencoderに関してdecoderの対数尤度を求めているということです。これからわかるように当然この式は最大化したいものということです。

画像の各ピクセルを0~1に調整し、ベルヌーイ分布を仮定するとlog{\displaystyle p(X|z)}は以下のようになります。

f:id:Parco1021:20200225213927p:plain

{\displaystyle y}はNNの出力です。

 

 

第1項は潜在変数の事前分布とencoderの分布の差を示す。正則化項。

第2項は入出力の差。

 

 

最後に図としてまとめます。

f:id:Parco1021:20200225220354p:plain

このφおよびθを学習します。 

 

しかしこのままだと確率分布を経由しているため誤差逆伝播法を使用することができません。なので{\displaystyle ε~N(0,I)}としてノイズを発生させ、{\displaystyle z=μ+εσ}とすることで確率分布を経由せずに誤差を逆伝播させることが可能となります。この手法をReparameterization Trickといいます。

 

さいごに

いかがでしたでしょうか。僕自身この確率分布だ~なんだ~の部分はとても苦手としているので非常に理解に時間がかかりました。VAE自体は少し前の技術なのですが今もなお研究が行われているそうですね。僕の卒業研究の選択肢の1つとなりそうです。

是非とも上記のQiita等もご覧になって多くの記事を参考にしてみてください。添え字等間違っていることがあったら(こっそり)教えてください。

 

 

おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

 

線形基底関数モデルの最適解

はじめに

前回、D次元の線形回帰モデルを解きました。

parco1021.hatenablog.com

話を1次元に戻します。線形回帰モデルを解くとイメージとしては下図のようになります。

f:id:Parco1021:20200206165132p:plain



{\displaystyle y=ax+b }の形ですね。しかしこの分布を見ているとこのようにも見えると思います。

f:id:Parco1021:20200206165016p:plain

汚くてすみません!!

このように、直線よりも曲線を用いた方がより分布に合っています。基底関数を用いてこのような曲線の関数を導出することが線形基底関数モデルの考え方です。

基底関数

まず基底関数について説明します。読んで字の如く関数を表現するためのベース、つまり基底となる関数です。もっとざっくり言うとグニャグニャしている線(曲線)を基底関数を組み合わせて表現しよう!といった感じです。…???同じことを言ったような気がしますが次第にわかると思います。

ガウス基底

{\displaystyle Φ_j(x)=exp\left\{-\frac{(x-μ_j)^2}{2s^2}\right\} }

f:id:Parco1021:20200206171820p:plain

引用元:ガウス関数

ただのガウス関数(正規分布)ですね。{\displaystyle s }は分散のようなイメージでいいと思います。つまり大きくするとそれだけ広範囲に影響が出ます(=グラフの山が潰れて広がるイメージ)。広範囲に影響を及ぼすことはよろしくないので小さい方が望ましいと思います。小さすぎるのもアレだけど。。。{\displaystyle μ_j }ガウス関数の中心位置ですね(山のテッペンの位置)。{\displaystyle s,μ_j }ともに設計者が決めるパラメータで変数は当然{\displaystyle x }のみです。

 

多項式基底

{\displaystyle Φ_j(x)=x^j }

つまり{\displaystyle Φ(x)=1,x,x^2,x^3・・・ }となるわけです。こちらの方が馴染みがあるので扱いやすいかもしれません。

 

解いてみよう

まず、今回解く対象である線形基底関数モデルを明記します。

{\displaystyle y(x,\boldsymbol{w})=\sum_{j=0}^{M} w_jΦ_j(x)=\boldsymbol{w}^T\boldsymbol{Φ}(x) }

ここで、{\displaystyle M }対象となるデータを何分割して基底関数を使うかを示しています。そして{\displaystyle w }は重みを表しています。つまり、M=3であるとしたら対象のデータをx軸方向に3分割して分割したそれぞれに適した重みwを計算します。そして導出した最適な重みを先ほど述べた基底関数に掛けることで曲線を実現させます。当然分割した各々は干渉し合います。

…わかりにくいですね。図示します。

f:id:Parco1021:20200206203406p:plain

 

フリーハンドですみません…。このように、M=3であるため3つのガウス基底で表現を試みます。この3つ各々に重みwを掛けて"いい感じ"にします。最後の項{\displaystyle w_3とΦ_3(x) }は係数のための処理です。

 

それでは、重み{\displaystyle \boldsymbol{w} }の最適解を求めていきましょう。タイトルの通り平均二乗誤差Jを使います。

{\displaystyle J(\boldsymbol{w})=\frac{1}{N}\sum_{n=0}^{N-1} (y(x,\boldsymbol{w})-t_n)^2=\frac{1}{N}\sum_{n=0}^{N-1} (\boldsymbol{w}^T\boldsymbol{Φ}(x)-t_n)^2 }

この形、どこかで見覚えがありませんか???そう、線形回帰モデルと同じ形をしています。

{\displaystyle J(\boldsymbol{w})=\frac{1}{N}\sum_{n=0}^{N-1} (\boldsymbol{w}^T\boldsymbol{x}_n-t_n)^2 }parco1021.hatenablog.com

 

{\displaystyle \boldsymbol{x}_nが\boldsymbol{Φ}(x) }へ変わっただけですね。なのでほとんどの処理は線形回帰モデルのものを応用できそうです。

  • 1次元データ{\displaystyle x_nをM次元(≒何分割するか)のベクトル\boldsymbol{Φ}(x_n) }に変換
  • {\displaystyle M次元入力された各入力\boldsymbol{x}_n }に対して線形回帰モデルを解く

以上のようにすることで線形回帰モデルと同様に処理することが可能であると考えられます。

つまり{\displaystyle 重み\boldsymbol{w} }の最適解は

{\displaystyle \boldsymbol{w}=(\boldsymbol{Φ}^T\boldsymbol{Φ})^{-1}\boldsymbol{Φ}^T\boldsymbol{t} }

となります。但し、

f:id:Parco1021:20200206202538p:plain

このような基底関数をN行M列に並べたものを計画行列(デザイン行列)と言います。

今回はxを一次元入力としましたが、仮にxが多次元であっても{\displaystyle x_n→\boldsymbol{x}_n }とすることで同様に扱うことができます。

 

参考文献

Pythonで「線形回帰」と"確率版の線形回帰"である「ベイズ線形回帰」 - Qiita

第9回 線形回帰[後編]:機械学習 はじめよう|gihyo.jp … 技術評論社

計画行列(デザイン行列)とは何か:PRML編 - 北野坂備忘録

ガウス基底モデル(μ, σランダム調整モデル)

 

 

おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

 

D次元線形回帰モデルを平均二乗誤差を用いて解く

※自分のはてなブログの編集都合上、行列表現がTexで表現できないため画像を挿入します。すみません。※

導入

線形回帰モデルとは

例えば直線モデルについて考えてみます。以下の図を見てください。

 

f:id:Parco1021:20200203134035p:plain

引用元:線形単回帰分析の仕組みをわかりやすく解説! | 全人類がわかる統計学

 

散らばっている点を"それっぽい"直線にしています。直線の式であるので{\displaystyle y(x)=w_0x_o+w_1 }といった形になっています。この{\displaystyle w_1およびw_0 }を求めることが「線形回帰モデルを解く」ということです。

 

本記事の目的

では、本記事の目的を明確にします。先に紹介した直線モデルは1次元モデルであり、それぞれ{\displaystyle w_0,w_1 }を求めることは難しくないです。しかしこれが2次元、3次元あるいはより高次元となった場合にも解くことは可能でしょうか?逐一解いてみても良いと思いますが本記事では高次元である{\displaystyle D }次元を想定します。

今回解く対象の{\displaystyle D }次元線形回帰モデルを示します。

{\displaystyle y(\boldsymbol{x})=w_0x_0+w_1x_1+・・・+w_{D-1}x_{D-1}+w_D }

{\displaystyle w_D }は切片を表します。今回は切片を省略し、以下の式を対象とします。

{\displaystyle y(\boldsymbol{x})=w_0x_0+w_1x_1+・・・+w_{D-1}x_{D-1} }

対象の式をより簡略化して書きたいと思います。行列表記を用いると

f:id:Parco1021:20200203155302p:plain

この{\displaystyle \boldsymbol{w} }が今回着目する文字です。所謂"重み"に似たやつです。

 

では早速解いてみましょう。今回、平均二乗誤差{\displaystyle J }で解きます。

f:id:Parco1021:20200203161319p:plain

ここで目的を明確にしておきます。目的は{\displaystyle y(\boldsymbol{x}) }を誤差の少ない関数とすることです。具体的には損失関数{\displaystyle J }を最小とする最適な{\displaystyle \boldsymbol{w} }を見つけることです。最小とする、つまりみんな大好き微分をします。

行列の計算に慣れていない方はここから紙とペンで実際に解きながら読むことをおすすめします。

 

解く

では早速{\displaystyle w_i }微分しましょう。

f:id:Parco1021:20200203180553p:plain

{\displaystyle w_i }微分すると{\displaystyle x_{n,i} }となることに注意すると

f:id:Parco1021:20200203180052p:plain

となります。注意ですが{\displaystyle x_{n,i} }のnはデータ数、iは(0~D-1)をとります。

{\displaystyle Jを最小にするwは、全てのw_iに対して傾きが0すなわち偏微分したら0となります。このwを求めます。 }

f:id:Parco1021:20200203181517p:plain

このまま{\displaystyle \boldsymbol{w} }について解いてしまいたいですが{\displaystyle x_{n,i} }が邪魔ですね。この方程式は全ての{\displaystyle i }において成り立つため、

f:id:Parco1021:20200203182027p:plain

 となります。ここからさらにわかりやすくするためにベクトル表現でまとめると、

f:id:Parco1021:20200203182555p:plain

簡略化すると、

f:id:Parco1021:20200203183045p:plain

分配法則を用いて、

f:id:Parco1021:20200203183346p:plain

f:id:Parco1021:20200203184353p:plain

このままではΣが邪魔で解きにくいですね。行列表現を用いてΣを消すように試みましょう。

f:id:Parco1021:20200204121601p:plain

f:id:Parco1021:20200204122042p:plain

新しく{\displaystyle \boldsymbol{X} }を導入することでΣを無くすことができました。

では、同様にして第2項も簡略化しましょう。

f:id:Parco1021:20200204123603p:plain

Σを消すことができたので元の方程式を置き換えてみます。

f:id:Parco1021:20200204124749p:plain

あとはこれを{\displaystyle \boldsymbol{w} }について解きます。まずは両辺転置を取ります。

f:id:Parco1021:20200204170848p:plain

これをガンガン解いていきます。

f:id:Parco1021:20200204172327p:plain

これがD次元線形回帰モデルの解です。この式を用いるとxが何次元であっても、すなわちDが何であっても最適な{\displaystyle \boldsymbol{w} }が得られます。お疲れさまでした。

切片を無視していましたね。あと少しです。

切片がある時

では、切片がある時を考えましょう。切片がある時ってどういう時でしょう?初めに示した式を再掲します。

{\displaystyle y(\boldsymbol{x})=w_0x_0+w_1x_1+・・・+w_{D-1}x_{D-1}+w_D }

これを以下のように見ます。

{\displaystyle y(\boldsymbol{x})=w_0x_0+w_1x_1+・・・+w_{D-1}x_{D-1}+w_Dx_D }

この{\displaystyle w_Dが切片ですね。このw_Dを常に出力させたいのでx_Dを常に1とすれば良いのです。このようにD+1次元目のxを常に1とすることで切片を実現させることが可能です。 }つまりD次元線形回帰モデルの解は変えなくて良いです。

 

さいごに 

行列の諸々を忘れていて理解に少し時間がかかってしまいました。しかし1つ1つ紐解いていくと徐々に理解ができて楽しかったです。

 

まとめ

D次元線形回帰モデル

{\displaystyle y(\boldsymbol{x})=w_0x_0+w_1x_1+・・・+w_{D-1}x_{D-1} }

f:id:Parco1021:20200203155302p:plain

の解は

f:id:Parco1021:20200204175037p:plain

但し、

f:id:Parco1021:20200204175133p:plain

 

参考文献

最小二乗法の解の導出 - 機械学習に詳しくなりたいブログ

 

 

おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

 

 

 

 

誤差逆伝播法についてできるだけ丁寧に初心者がまとめる

導入

はじめに

勾配法において、損失関数を重みにおいて偏微分しそれを用いて重みを更新します。その手法の一種として以前に数値微分法という微分の定義に基づき、近似を用いることで勾配を求める手法をまとめました。今回は勾配を求める手法の1つとして誤差逆伝播法についてできるだけ丁寧にまとめたいと思います。僕自身その道について未だ初心者であるので同じような方の助けになれば幸いです。

parco1021.hatenablog.com

 

おねがい

本記事は勉強中の筆者が書いたものです。ゆえに少し解釈違いがあったり、文字が違っていたりするかもしれません。そういった場合にはコメントまたは下の問い合わせフォームから教えていただけると幸いです。

また、誤差逆伝播法を学ぶにあたり文章を読むだけでなく手元に紙とペンを用意して数式を書きながら読んでいただければ理解が捗ると思います。

誤差逆伝播法って聞くと…??

僕は未だ自分でモデルを組む等したことがありません。なので誤差逆伝播法についての認識は"勾配を求めるいい感じの方法"ぐらいでしかないです。その認識は実際に使ってみないと変わらないと思うので今のところはこれで良しとします。

大学の講義や教科書でのBackpropの説明はほとんど,「教師あり学習の文脈で多層パーセプトロンを識別器あるいは関数近似器として訓練する」という文脈でなされます.そのため,初学者はBackpropは教師あり学習のためのアルゴリズムであると誤解してしまうケースが多々あるのではないかと思います.しかし後で説明する通り,Backpropは単に損失関数の微分を効率的に計算する手法の1つであって教師あり学習とか教師なし学習とかは関係ありません

引用元:https://qiita.com/Ugo-Nama/items/04814a13c9ea84978a4c#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB

だそうです。僕は講義で習っておらず、現場でも使っていないのでその誤認はしていないので気を付けます。

ニューラルネットモデル

今回扱うニューラルネットモデルを以下に示します。

f:id:Parco1021:20200112181056p:plain

 今回は、簡略化のために中間層を1層とします。それぞれの変数の定義をします。

  • {\displaystyle x }…入力
  • {\displaystyle v,w }…重み
  • {\displaystyle h(),g() }…活性化関数
  • {\displaystyle b_j=\sum_{i=0}^{D} w_{ji}x_i }

→前の層からの出力(x)に重み(w)を掛けたものを全ての矢印において行っている

  • {\displaystyle z_j=h(b_j) }

→先ほど求めたbを活性化関数に引数として渡す。これにより求めたzが次の層へ渡される

  • {\displaystyle a_k=\sum_{j=0}^{M} v_{kj}z_j }
  • {\displaystyle y_k=g(a_k) }
  • {\displaystyle t_k }…教師データ

{\displaystyle δ }については後述します。

 

誤差逆伝播

では、誤差逆伝播法についてやっていきましょう。誤差逆伝播は簡単に言うと、出力層の重み{\displaystyle v }を更新してから中間層の重み{\displaystyle w }を更新するように、入力層→出力層ではなく、出力層→入力層へと流れていく様子からこの名前が付けられました。バックプロパケーション誤差逆伝搬法とも呼ばれます。

最終的にバックプロップによって最小化したい損失関数は以下のようになります。

{\displaystyle E(v,w)=\frac{1}{N}\sum_{n=0}^{N-1} E_n(v,w) }

この式の意味するところは損失関数を学習に使うデータ数{\displaystyle N }だけ足して平均したものが目的となる関数であるということです。

この関数を最小化するために勾配を求めます。つまり各データ{\displaystyle n }に対する勾配{\displaystyle \frac{∂E_n}{∂w} }を得なければなりません。求めて平均をとることで{\displaystyle \frac{∂E}{∂w} }が得られます。当然、{\displaystyle wだけでなく、v }における勾配も求める必要があります。

{\displaystyle \frac{∂E}{∂v} }を求める

連鎖律を用いて{\displaystyle \frac{∂E}{∂v}を求めます。 }

{\displaystyle \frac{∂E}{∂v}=\frac{∂E}{∂a}\frac{∂a}{∂v} }

となるのでそれぞれ分解します。繰り返しになりますが、{\displaystyle v }{\displaystyle jk }間の重みであり、{\displaystyle aはj層 }からの入力総和です。

まず、

{\displaystyle \frac{∂a}{∂v}=\frac{∂}{∂v_kj}(\sum_{j} v_{kj}z_j)=\frac{∂}{∂v_kj}( v_{k0}z_0+v_{k1}z_1+…v_{kj}z_j) }=z_jとなります。

次に、

{\displaystyle δ^{(2)}_k= \frac{∂E}{∂a} }とすると

{\displaystyle \frac{∂E}{∂v_kj}=δ^{(2)}_kz_j}となり簡単な式になりました!

f:id:Parco1021:20200130171048p:plain>ちょっと待ちなさい!{\displaystyle δ^{(2)}_k}っていったいなんなのよ!

ごめんなさいお母さん!!

{\displaystyle δ^{(2)}_k= \frac{∂E}{∂a}=\frac{∂E}{∂y}\frac{∂y}{∂a}=\frac{∂E}{∂y}g'(a) }

{\displaystyle \frac{∂y}{∂a} }{\displaystyle y=g(a) }となっているので当然ですね。例えば損失関数を二乗誤差とし、係数など諸々を無視する{\displaystyle \frac{∂E}{∂y}は大体y-t }となります。{\displaystyle yは出力であり、tは教師データです。}ゆえに概要としては結合先で生じた誤差に関する変数といった感じです。

つまり、{\displaystyle \frac{∂E}{∂v_kj}は、}結合元のニューロンの出力{\displaystyle z_j}と結合先での誤差{\displaystyle δ^{(2)}_k}を掛けたものであることがわかります。

重みの更新規則は

{\displaystyle v^{r+1}_{kj}=v^r_{kj}-α\frac{∂E}{∂v_{kj}}=v^r_{kj}-αδ^{(2)}_kz_j}となります。{\displaystyle αは定数です。}

以上で重み{\displaystyle v}については終わりです。次は中間層周りの重み{\displaystyle w}についてです。

 

{\displaystyle \frac{∂E}{∂w} }を求める

では{\displaystyle \frac{∂E}{∂w}}を求めていきましょう。数式が一見複雑そうに見えますが、内容としてはこれまでと同様レベルです。一緒に頑張りましょう。

{\displaystyle \frac{∂E}{∂w}=\frac{∂E}{∂b}\frac{∂b}{∂w} }となります。

ここで先ほどと同様に結合先への総和{\displaystyle b}があるので

{\displaystyle \frac{∂E}{∂b}=δ^{(1)}_j}とします。

{\displaystyle \frac{∂b}{∂w}=\frac{∂}{∂w_ji}(\sum_{i} w_{ji}x_i)=\frac{∂}{∂w_ji}( w_{j0}x_0+w_{j1}x_1+…w_{ji}x_i) =x_i}

よって

{\displaystyle \frac{∂E}{∂w}=δ^{(1)}_jx_i }となり、先ほど同様簡単な形になりました!

f:id:Parco1021:20200130171048p:plain

 

…はい。例のごとく{\displaystyle δ^{(1)}_j}が何を指すのか調査しましょう。k層があるのでΣを用います。

{\displaystyle δ^{(1)}_j=\frac{∂E}{∂b}=\sum_{k}\frac{∂E}{∂y_k}\frac{∂y_k}{∂a_k}\frac{∂a_k}{∂z_j}\frac{∂z_j}{∂b_j}}

{\displaystyle \frac{∂E}{∂y}\frac{∂y}{∂a}=δ^{(2)}_k }であり、{\displaystyle \frac{∂a}{∂z}=v_{kj} }であり、さらに{\displaystyle \frac{∂z}{∂b}= h'(x)}となります。

これらを用いると{\displaystyle δ^{(1)}_j }

{\displaystyle δ^{(1)}_j=h'(b_j)\sum_{k}v_{kj}δ^{(2)}_k }となります。

この式と以下の図に着目してみてください。本来のNNは入力層→出力層(左から右)へと処理が為されるのに対し、中間層の{\displaystyle δ^{(1)}_j}を求めるために出力層から{\displaystyle δ^{(2)}_k}と中間層~出力層間の重み{\displaystyle v}を掛け合わせたものを逆方向へ伝達させていることがわかります。

{\displaystyle δ^{(2)}_k}は先ほども述べた通り出力層の誤差です。誤差を逆方向へ伝播させていることから誤差逆伝播法と言います(と思います)。

 

f:id:Parco1021:20200201203215p:plain

{\displaystyle \frac{∂E}{∂w}が求まったので重みwの }更新規則を書きます。
{\displaystyle w^{r+1}_{ji}=w^r_{ji}-α\frac{∂E}{∂w_{ji}}=w^r_{ji}-αδ^{(1)}_jx_i}

 

ここまで、誤差逆伝播法について数式を交えつつ解説してきました。次はより抽象的に図を主としてイメージを掴みます。

 

誤差逆伝播法のまとめ

①NNにxを入力し、b,z,a,さらに出力yを得て全て保持しておく

入力し出力を得るのでここは普通に順伝播(左から右)

f:id:Parco1021:20200201210825p:plain

 

 

②教師データtと出力yから出力層の誤差δを計算する。そして逆方向へ伝播し、重みvと乗算しΣ計算することで中間層の誤差を計算する

f:id:Parco1021:20200201212023p:plain

 

③δを用いて重みを更新する 

f:id:Parco1021:20200201213547p:plain

 

さいごに

いかがでしたでしょうか。数式は多かったと思いますが1つ1つ丁寧に追っていけば大学1年生レベルのものであることがわかると思います。誤差逆伝播法を使うと何がメリットで何がデメリットなのかわからないので今後調べていきたいと思います。例えば誤差逆伝播法は"人間っぽい"考え方ではないから別の手法を考えていると研究室の先輩が輪講で言ってたような言ってなかったような…みたいな。

 

参考文献

qiita.com

 

 

おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

Pythonでcsvを分割して読み込む(改訂版)

はじめに

半年ほど前に似たような記事を書いたのですが、それは自分が使用したコードを貼り付けただけで全く参考にならなさそうだったので簡略化したものをもう一度書くことにしました。

前回の↓

Python3で大容量のcsvファイルを分割して読み込む - 情報学部大学生のダラダラ日記

 

使用するcsvとコード

スクリーンショットで失礼します。以下のcsvを使います。

f:id:Parco1021:20191228182704p:plain

import pandas as pd

reader = pd.read_csv('tagA.csv'chunksize=5)

for r in reader:
    print(r)

chunksizeなどの説明は以前の記事に書いたのでそちらをご覧になってください。簡単に言うとchunksizeで指定した数で分割して読み込みます。なのでこのコードだと5個ずつデータを分割します。そして今回データ数は10個なので10/5で2回に分けて読み込みます。

 

結果

f:id:Parco1021:20191228181452p:plain

ご覧の通り5個ずつ2回に分けて読み込んでいることがわかります。

もう少しいじってみる

分割して読み込んだのはいいとして、これを少しいじってみます。

コード
import pandas as pd

reader = pd.read_csv('tagA.csv'chunksize=5)

for r in reader:
    row = []
    for index,rows in r.iterrows():
        print(rows[1])
        row.append(rows[1])
    print("#分割の区切り")
    print(row)

iterrows()とすることで列ごと見ることができます。具体的にはこのコードにおけるrowsはrow[0]にid、row[1]にはtagが入っています。これをさらに別々に出力させることもでき、今回はリストにも入れてみました。

出力

f:id:Parco1021:20191228185655p:plain

今回はfor文の中でリストを初期化しているのでこのような出力となっていますが、外でやった場合、全データのtagのリストとなります。また、iterrows()をiteritems()とすることで行で見ることができるので用途によって使い分けが必要です。
また、今回出力させていませんが、indexにはインデックス(0,1,...9)が入っています。

さいごに

使用機材のメモリにのらない時に多く使われる印象のあるchunksizeというオプションですが、考えようによっては他にも良い使い方がありそうだな~と思いました。

 

おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

 

2019年の終わり、それはPython2の終わり。

僕は知らなかったのですが、Python2がサポート終了するのが2020年1月1日に大分前から決まっていたようです。公式のドキュメントはこちら↓

www.python.org

「Python2の日没」ですって。オシャレ。

ドキュメントによると、2000年にPython2が始まり、2006年にPython3が作られた?のですが、初めはPython3がよろしくなかったらしくPython3へ移行する人があまりおらず、結局Python2を使う人とPython3使う人が共存してしまい、両方改良を続けていたようです。

こうなってしまった原因として、2系と3系で互換性がないことが挙げられます。僕も最初ネットを参照しながらPythonを書いていたらネットには2系と3系が混在しており、エラーが無くならず心が折れかけました。例としては2系ではprint "Hello World"であるのに対し、3系ではprint ("Hello World")のようになります。僕は3系しか使ったことがないので他にはわかりませんが、多くの違いがあり、全くの別言語らしいです。

話を戻して、両方改良するのはさすがに公式さんも大変だったらしく、2008年に「2015年にサポートを終了する」というアナウンスをしたそうです。しかし、2014年にそれは厳しいと判断し、2020年に終了すると先延ばしにしました。

今は2019年12月末。あと少しでPython2のサポートが終了します。Python2しか使ってない!って人も、2to3というPython2を3へ変更するモノがあるらしいので応急措置ですが大丈夫です。あくまで応急措置なのでPython3のお勉強をしなければなりませんが…。

あとどれだけでサポートが終了するか示すサイトもあるぽいです。

Python 2.7 Countdown

あと10日と4時間です。

f:id:Parco1021:20191221193057p:plain

 

おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

 

数値微分法についてまとめてPythonで色々してみる

 勾配法あたりでやたらと見る数値微分法について調査し、まとめてみました。

 今回、理論的なことは以下の書籍を参照し、図とプログラムは1から自作したものであり、特にプログラムは筆者がその場で簡易的に考え実装したものです。変数名などおかしい点がありますがご容赦ください。

数値微分法とは

 勾配法においてはある重み{\displaystyle w_1 }において、誤差関数{\displaystyle E(w) }{\displaystyle w }に関して偏微分したものの符号を入れ変えたもの{\displaystyle -\frac{∂E}{∂w} }の方向に重み{\displaystyle w_1 }を更新します。この偏微分という動作をしなくても近似を使うことによって重みの更新をしちゃおう!というのが数値微分法の目的です。

 

 まず、微分の定義を思い出します。

{\displaystyle \lim_{h \to 0} \frac{f(x+h)-f(x)}{h} }

これが見慣れた形ですが今回は文字を変えて

{\displaystyle \lim_{ε \to 0} \frac{E(w_1+ε)-E(w_1)}{ε} }

とします。

f:id:Parco1021:20191215170236p:plain

余談ですが、式だけだとわかりづらいので僕が誰かに微分の定義を説明する時は上みたいな図を描いてこのεを0にもってくとその点での接線になるでしょ??って言います。

 

まず、数値微分法のうち、2点近似を紹介します。図は↑と同様で、式は

{\displaystyle \frac{E(w_1+ε)-E(w_1)}{ε} }です。

 数値微分法では極限などを使わず、手動で限りなく小さいεを設定することで勾配を近似します。例えばεを0.001と設定し、{\displaystyle E(w)=w^3 }{\displaystyle w_1=3 }とすると、

微分法:{\displaystyle E'(3)=27 }

数値微分法(2点近似):{\displaystyle E'(3)≒27.009001‬ }となり、誤差は約0.01ほどになります。

 

 では、より誤差を減らす方法はないの?と思ったそこの奥様!あります。三点近似(中心差分)という手法です。図は先ほどと異なります。

f:id:Parco1021:20191215173433p:plain

そして式は

{\displaystyle \frac{E(w_1+ε)-E(w_1-ε)}{2ε} }です。

試しに先ほどの2点近似と同様の条件で計算してみると

数値微分法(3点近似):{\displaystyle E'(3)≒27.000001‬‬ }となり、誤差が0.000001にまで減りました。

 

Pythonで観測

式自体は単純なものなので書く必要もないと思いますが、通常の微分したものとの差をグラフに描画するプログラムを書きます。

import matplotlib.pyplot as plt
%matplotlib inline

Ew = lambda w: w**3
Gw = lambda w: 3*w**2
e = 0.001
w = [-10000,-100,0,2,16,64,100,1000,10000]
_two = []
_three = []

def normal(w):
    E_dashn = Gw(w)
    return E_dashn
    
    
def three_point(w,e):
    E_dashth = (Ew(w+e)-Ew(w-e)) / (2*e)
    return E_dashth

def two_point(w,e):
    E_dashtw = (Ew(w+e)-Ew(w)) / e
    return E_dashtw

for i in range(len(w)):
        _two.append(two_point(w[i],e)-normal(w[i]))
        _three.append(three_point(w[i],e)-normal(w[i]))

plt.plot(w,_two,label='2point')
plt.plot(w,_three,label="3point")
plt.show()

プログラムは単純なものです。式Ewとそれを微分した式Gwを用意し、wを変化させた時の2点近似と3点近似と通常微分との値の差を取っています。

 

結果

f:id:Parco1021:20191215192757p:plain

グラフを観ると、2点近似は単調増加しており、3点近似は変化していません。これは今回の{\displaystyle E(w)がw^3}であることと、それぞれの式を観れば自明です。そして2点近似と3点近似の優劣は関数{\displaystyle E(w) }が変わっても変化しないでしょう。よって計算量が増加するというデメリットがありますが、3点近似を使う方がより誤差を減らすことができます。

参考文献

 

 

 おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

2次方程式や3次方程式の解と係数の関係の導出方法

 解と係数の関係、知っていますでしょうか。結構な頻度で使われるので受験を経験した人達は覚えていると思います。しかしセンター試験レベルだとせいぜい使っても2次方程式の解と係数の関係ぐらいなので暗記でも乗り越えられるのですが、突然3次方程式の解と係数の関係を使う問題がでた時に困ります。そんな時に導出方法を知っていると役に立つと思うので知らない受験生は知り得です。

2次方程式の解と係数の関係

2次方程式{\displaystyle ax^2+bx+c=0 }が2つの解{\displaystyle α,β }を持つとき、

{\displaystyle α+β=-\frac{b}{a} }

{\displaystyle αβ=\frac{c}{a} }

が成り立ちます。僕は「マイナス(-)を忘れる奴はば(ba)か(ca)」と覚えろと教わりました。まず、2つの方法で2次方程式における関係を導出してみます。

 

 

1つ目は解の公式を使います。

 x=\frac{-b±\sqrt{b^2-4ac}}{2a}

この場合、解は{\displaystyle α,β }なので

{\displaystyle α+β=\frac{-b+\sqrt{b^2-4ac}}{2a}+\frac{-b-\sqrt{b^2-4ac}}{2a}=\frac{-2b}{2a}=-\frac{b}{a}}

{\displaystyle αβ=\frac{b^2-(b^2-4ac)}{4a^2}=\frac{c}{a} }

となります。

 

2つ目は因数定理を使います。

※因数定理

{\displaystyle f(x)が(x-α)を因数(掛け算として表したときにその個々の数のこと)持つことはf(α)=0となるための必要十分条件である }

 

これを使うことにより

{\displaystyle ax^2+bx+c=0がα,βを解に持つ⇔A(x-α)(x-β)=0 }とすることができます。

右辺を展開すると

{\displaystyle Ax^2-A(α+β)x+Aαβ=0 }となります。そこで元の式と比較すると

{\displaystyle a=A }

{\displaystyle b=-A(α+β) }

{\displaystyle c=Aαβ }となるのでここから

{\displaystyle α+β=-\frac{b}{a} }

{\displaystyle αβ=\frac{c}{a} }が求められます。

 

2つ導出方法を紹介しましたがどちらを覚えるべきでしょうか?どちらも覚えてほしいですが重要なのは2つ目です。理由は解の公式は4次式までしかありませんし、とてもじゃありませんが覚えるなんて不可能に近いです。とりあえず2つ目の方法を使って3次方程式の解と係数の関係を求めてみます。

{\displaystyle ax^3+bx^2+cx+d=0 }が3つの解{\displaystyle α,β,γ }を持つとき、因数定理より

{\displaystyle A(x-α)(x-β)(x-γ)=0 }となります。展開すると

{\displaystyle Ax^3-A(α+β+γ)x^2+A(αβ+βγ+γα)x-Aαβγ=0 }となるので係数比較すると

{\displaystyle α+β+γ=-\frac{b}{a} }

{\displaystyle αβ+βγ+γα=-\frac{c}{a} }

{\displaystyle αβγ=-\frac{d}{a} }となります。

まだ2次と3次しか見ていませんが何か規則性が"観えて"きますね。ムムッ

1次の時は{\displaystyle α=-\frac{b}{a} }ですし多分解を足したら同じ結果になるんでしょうね。あとは解を掛けたら定数項/aとか。

さいごに

 今回は手計算でごり押し風に3次までやりましたが5次あたりからきつくなると思います。なのでn次として一般化しなければならないのですが、そこは僕も受験生時代お世話になった以下のサイトさんがやっててくれました。

mathtrain.jp

上のサイトにあるならやらなくていいんじゃないかという声が聞こえてくる気がしますが、高校数学あたりの公式の証明って良い頭の体操になるんですよ。それに"調べれば"出てくるという仮定つきなので調べなくても誰かの目に留まればいいなって感じです。自己満です。頭の体操としてやったのでサイトを観ていません。なのでもし間違っているところ等あればこっそり教えてください。

 

 おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

予測損失と経験損失について簡単にまとめる

 機械学習の大きな目的は訓練データから未知のデータ、すなわちテストデータに対する損失を小さくすることです。予測損失(テストデータ)と経験損失(訓練データ)についてまとめてみます。

予測損失

 予測損失というよりも汎化誤差という言葉の方が親しみがあるのではないでしょうか。また、他にも期待損失などとも言うようです。仮説{\displaystyle h }の予測損失{\displaystyle R(x) }が、損失関数として{\displaystyle l }、テストデータ{\displaystyle (X,Y) }の分布{\displaystyle D }を用いて以下のように定義されます。

{\displaystyle R(h) = E_{(X,Y)~D}[l(h(X),Y)]}

つまり、テストデータの分布に対してのテストデータの損失の期待値ということです。しかし、テストデータの分布は未知であるためこの予測損失{\displaystyle R(x) }を計算することはできないです。目標は観測データのみからできるだけ{\displaystyle R(x) }を小さくする仮説{\displaystyle h }を求めることです。

経験損失

 標本誤差、経験誤差とも言われています。データ{\displaystyle (X_1,Y_1),(X_2,Y_2),.....(X_n,Y_n) }が観測され、仮説と損失関数を先ほどと同じ文字に設定すると観測データに対する仮説{\displaystyle h }の経験損失{\displaystyle R ̃(h) }は以下のようになります。

 {\displaystyle R ̃(h) =∑_{(i=1)}^nl(h(X_i),Y_i) }

先ほどの予測損失と異なり、観測データは明らかになっているので{\displaystyle R ̃(h) }は求めることが可能です。

 特にこの時、確率{\displaystyle 1/n }{\displaystyle (X_i,Y_i) }を取る時、確率分布を{\displaystyle D_1 }とすると上式は

{\displaystyle R ̃(h) = E_{(X,Y)~D_1}[l(h(X),Y)] }

と表すことができる。この式は予測損失の式ととても似ていますね。違いは従っている確率分布です。つまり、データが同一の確率分布に従うとき、

{\displaystyle R ̃(h) ≒ R(h) }

とすることができます。

結局???

 予測損失を最小にしたいのはわかっていただけたと思います。簡単に説明すると"予測損失を最小にすることが(実質的に)機械学習の主たる目的だから"です。ではなぜ経験損失を求める必要があるのでしょうか?それは上でも述べた通り、経験損失と予測損失が近似できるからです。流れは以下のような感じです。

 

予測損失が最小となるよう仮説を設定したい

テストデータの分布は未知であるため値が求められない

予測損失と近似できる経験損失がある

経験損失は実測値から求められる

 

という感じです。今回は理論的な部分から予測損失(汎化誤差)にアプローチしてみましたが、応用的な部分から観てみるとなぜ予測損失を最小にしたいかが感覚的にわかってくると思います。以下のサイトをご覧になってみてください。

訓練誤差と汎化誤差の理論計算 - Qiita

参考文献

以下の書籍を参考に書きました。

 

 

 おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

 

モデルを蒸留するのではなくデータセットを蒸留する(論文紹介②Dataset Distillation)

蒸留とは

 中学生の時に化学で学んだ蒸留について、

蒸留(じょうりゅう、Distillation)とは、混合物を一度蒸発させ、後で再び凝縮させることで、沸点の異なる成分を分離・濃縮する操作をいう。

引用元:Wikipedia

 深く、大きいモデルが優秀であることは想像に難くありません。しかし、実際にはそのような大きいモデルが使用できないが機械学習モデルを使用したい場面があります(ラズパイとかでやる時など)。そのような時に、深く大きいニューラルネットワークの知識を蒸留し、より浅く小さいニューラルネットワークへ学習させるために使われるものです。特に大きいモデルを教師モデル、小さいモデルを生徒モデルと言います。つまり性能をできるだけそのままに教師モデルから生徒モデルへ知識の継承を行うことを目的としています。これについての元論文は以下です。

Distilling the Knowledge in a Neural Network,Geoffrey Hinton, Oriol Vinyals, Jeff Dean,2015,https://arxiv.org/abs/1503.02531

日本語でまとめられた文献もあります。

Deep Learningにおける知識の蒸留 | Code Craft House

論文紹介

今後、本記事での画像等の引用は全てこの論文からです。

引用数は9でタイトルはそのまま「データセットの蒸留」です。

概要

 先ではモデルの蒸留を行っていましたが本論文ではデータセットの蒸留をします。具体的には膨大なデータセットをサンプリングすることなく、各クラスごとに新たな画像を生成することでデータセットを圧縮している。

f:id:Parco1021:20191127182634p:plain

引用元:Dataset Distillation

 

イメージとしては上のような感じ。このように新たに画像を生成することで数万枚のデータセットが10~300枚へと蒸留することができている。

アルゴリズム

f:id:Parco1021:20191127184527p:plain

引用元:Dataset Distillation

<!--以下自己解釈

蒸留後データセット{\displaystyle x ̃ }を初期化する。

元データ{\displaystyle x }を取得する。

ある重み{\displaystyle }θ_0^{(j )}において勾配を求め、より良い重み{\displaystyle }θ_1^{(j )}を決定

元データと{\displaystyle }θ_1^{(j )}を使ってロス関数を計算する。

ロス関数の勾配から蒸留後データセット{\displaystyle x ̃ }を更新する。

-->

実験結果

まず、上記のアルゴリズムを繰り返し行う2パターンの実験を行う。

上のアルゴリズムを拡張して2つの方法を用意する。

(a)重み{\displaystyle θ }の更新を複数回行う

(b){\displaystyle x ̃ }の更新を複数回行う

f:id:Parco1021:20191127190345p:plain

引用元:Dataset Distillation

 

実験対象としてMNISTとCIFAR10が用いられたが、(a),(b)どちらの場合にも複数回行った方が正確となっている。

 

次に、ベースライン条件として以下の4つの条件を用意し、それとデータセットを蒸留した時の結果を比較する。

ベースライン条件

  1. ランダム
  2. 学習効果の高かった上位20%
  3. k-means法
  4. 各画像の平均

結果が以下です。ベースライン条件よりも良い結果になっていることがわかります。

f:id:Parco1021:20191127193422p:plain

引用元:Dataset Distillation 

さいごに

 今回は蒸留という手法の新しいアプローチを行った論文でした。どのようなクラスからどのような画像が生成されたかは参考文献の公式リンクから観てみてください。carクラスだったりが1つの画像になっているのは結構面白いです。

英語キチィ~

参考文献

 

 

 おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

【Python】Twitter apiを使って複数画像を投稿する

はじめに

 Twitter botを作成する際にapiを使って複数枚画像を投稿する方法があまりネットでは見つからなかったり、のっている情報が誤っていたりしたのでメモします。今回はいらすとやさんの画像を使いました。

プログラム

import requests

import twitter

import urllib.request

import requests

from requests_oauthlib import OAuth1Session

import json





CK=''

CS=''

AT=''

AS=''

twitter = OAuth1Session(CK,CS,AT,AS)



url_media = "https://upload.twitter.com/1.1/media/upload.json"

url_text = "https://api.twitter.com/1.1/statuses/update.json"



img_url = ['https://1.bp.blogspot.com/-SWOiphrHWnI/XWS5x7MYwHI/AAAAAAABUXA/i_PRL_Atr08ayl9sZy9-x0uoY4zV2d5xwCLcBGAs/s1600/pose_dance_ukareru_man.png',

            'https://1.bp.blogspot.com/-CSIokkL0VJc/XVKgHNKp2QI/AAAAAAABUHU/znkuxlOlQ5giZ3gDbks7KAK3TJnT2q1XwCLcBGAs/s1600/kotowaza_hato_mamedeppou.png',

            'https://1.bp.blogspot.com/-8sMAiPmvFuo/XVjgKN2BXoI/AAAAAAABUM0/IfTQp8hHWRsVk_u7s84OE6yvFJ5ekpnLwCLcBGAs/s1600/kid_seikaku_uchiki_girl.png',

            'https://1.bp.blogspot.com/-ahlT7Kd7-T0/XVjgJ3hrbFI/AAAAAAABUMw/MV4su85SnoAMYnSitR9DXVgNFuorpprwQCLcBGAs/s1600/kid_seikaku_uchiki_boy.png']  #①



def call():    

    media_id = []

    for i in range(4):

        headers = {"User-Agent": "Mozilla/5.0"}  #②

        request = urllib.request.Request(url=img_url[i],headers=headers)

        response = urllib.request.urlopen(request)

        data = response.read()

        files = {"media" : data}

        req_media = twitter.post(url_media,files = files)  #③

        media_id.append(json.loads(req_media.text)['media_id_string'])

    media_id= ','.join(media_id)  #④

    status = "投稿テスト"

    params = {"status": status, "media_ids": media_id}

    twitter.post(url_text,params=params)

    

        

if __name__ =='__main__':

    call()

解説と結果

  • ①ここで投稿したい画像のURLをリストにいれておいて準備します。僕はURLから画像を取得したかったのでやっていませんが、ローカルのファイルのパスを指定して投稿したい場合は他の方が多くやっていらっしゃるのでそちらを参照してください。
  • ②ユーザーエージェント(どのようなブラウザがサーバへアクセスしているかの情報)をこのようにFirefoxなどに指定しないと権限エラーが出てアクセスできないので注意してください。これについても多くの情報があるので調べてみてください。
  • ③②~③までの間でリスト内の画像URLをサーバから受け取りました。Twitterには直接画像のURLを画像として呟く事ができないので③ではこれをTwitter(url_media)へpostすることでURLを画像コード(数字の羅列)に変換しています。
  • ④今回の主たるところです。media_idが最終的にTwitterへ送る際にはじめはリストを渡せばいいと思っていましたが一枚しか投稿されませんでした。おそらく、['111','112',・・・]となっていることで途中のシングルクォーテーションが区切りと判断されたことが原因だと思います。なので④のようにリストの要素をカンマでjoin結合し、[111,112,・・・]とすることで複数枚投稿が可能となります。

 呟いた結果がこちら↓

f:id:Parco1021:20191123200647j:image

 

さいごに

 今回はTwitter apiの複数枚投稿についてまとめました。4で割り切れない場合などに臨機応変に対応させて複数枚投稿させたい場合はmodなどを使うといいです。僕が作ったbotはそのような設計になっているのでもしよければ観てみてください。

parco1021.hatenablog.com

(あげたあとにあげる必要ないって気づいたけど一応gitにもあげたので…https://github.com/tnb1021/tweetimgs)

参考文献

こちらのプログラムを拡張し、複数枚投稿できるようにしました。

qiita.com

 

 おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

最近月曜日と金曜日の雨多くない??

はじめに

 僕は今、月曜日と金曜日に実験の講義があります。そしてその時、軒並み雨のような気がしています。果たしてそれは僕が他の曜日に外出しないが故の勘違いなのかあるいは正しいのか検証してみます。

 '最近'と曖昧にしましたが、実験が始まったのが10月からなので10月1日~昨日(11月21日)までの情報を気象庁さんが提供してくださっているものをcsv保存しました。

f:id:Parco1021:20191122170019p:plain

データは概ねこのような感じです(一応データは隠しました)。csv保存せず、スクレイピングしてもよかったのですが曜日計算がめんどくさかったのと必要な情報が少なかったのでやりませんでした。

※筆者は統計学の学がないです。また、決して曜日と天気に因果関係があることを示唆しているわけではありません。

プログラム

 「このぐらいなら手で数えろよ…」の声が聞こえてきますが嫌です。書きました。

import pandas as pd

f = pd.read_csv('data.csv',encoding='cp932')
f.columns = ['day','dow','precipitation']

count_rain = 0
count_rainexp = 0
count_exp = 0
count_day = 0

for row_index,row in f.iterrows():
    if row[2] != '--':
        if float(row[2]) > 1:
            count_rain += 1
            if row[1] == '月' or row[1] == '金':
                count_rainexp += 1
    if row[1] == '月' or row[1] == '金':
        count_exp += 1
    count_day += 1

'''
for row_index,row in f.iterrows():
    if row[2] != '--':
        if float(row[2]) > 1:
            count_rain += 1
            if row[1] == '金':
                count_rainexp += 1
    if row[1] == '金':
        count_exp += 1
    count_day += 1
'''    

print('雨日:',count_rain)
print('実験で雨日:',count_rainexp)
print('実験日:',count_exp)
print('総合日数:',count_day)

 おなじみのpandasライブラリでf.iterrows()で一行ずつ参照しています。降水量が無の時を表す"--"でないとき他は全て数値なのでfloat型で変換しています。あとはそれぞれ求めたい数値をインクリメントしています。一応月金verと金のみverを書きましたが結果はほぼ同じでした。

実行結果

 今回、総合降水量で判断しているので多少誤差がありますが1mmを超えた時に雨日としました。

雨日: 17
実験で雨日: 7
実験日: 14
総合日数: 52

 つまり実験がある日が14/52、雨の日が17/52、実験の日かつ雨の日が7/52日ということになります。実験がある日という条件の下で雨が降った条件付き確率は7/14=1/2ということになります。さらに、11/4(月)と11/8(金)は変則日課で実験がありませんし雨も降っていません。これを加味すると条件付き確率は7/12≒0.58となります。さらにさらに今日11/22は実験でありさらに雨が降っています。よって8/13≒0.62となります。

さいごに

 補足すると雨かつ火or水は5日、雨かつ土or日は2日でした(ってことは木は3日)。つまり偏りがありますが今回そんなことはどうでもいいんです。重要なのは

ただでさえ憂鬱な実験の日に雨が6割も降って空気ジメジメするしより憂鬱になるんだよ!!!!

ってことです。

 後期も折り返しです。頑張りましょう。

 

 

 おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

Windowsで毎日定時にプログラムを実行する方法

はじめに

 Twitterbotを作るために毎日定時にプログラムを実行したいのでタスクスケジューラを使います。

parco1021.hatenablog.com

手順

  1. プログラム実行用のバッチファイルを用意する
  2. 1のバッチファイルをタスクスケジューラで実行する

まず、バッチファイルを用意します。これについては下の記事を参照してください。

parco1021.hatenablog.com

要するに実行したいコマンドが打ち込まれているファイルが拡張子.batで作られていればいいです。

 次にタスクスケジューラの設定をします。窓からタスクスケジューラを開きます。適当なライブラリで右クリック→タスクの作成を選択してください。名前を設定してから「ユーザがログオンしているかに関わらず実行する」にチェックします。これをしないと貴方がPCを開いていないとプログラムが実行されません。そして「最上位の特権で実行する」にもチェックをします。そしてトリガーを実行したいように設定します。

f:id:Parco1021:20191120152028p:plain

僕は9時ちょい過ぎに毎日実行したいのでこのように設定します。

操作はまず「プログラムの開始」を選択します。そしてプログラム/スクリプトは参照から作成したバッチファイルを選択し、開始(オプション)にはバッチファイルのディレクトリを設定します。これをしないと実行されないので注意してください。僕はこれをしていなくて躓きました。

f:id:Parco1021:20191120153041p:plain

そして次に条件において「タスクを実行するためにスリープを解除する」のチェックをします。

f:id:Parco1021:20191120153345p:plain

しかし、ここで設定しただけではスリープを解除することがPC本体において有効ではないのでそこの設定もいじらなければなりません。そこは以下を参照してください。

qiita.com

あとはデフォルトで大丈夫っぽいです。

さいごに

 Windows内のプログラムを定期実行したい場合はまず一回だけタスクスケジューラから実行できているか確認することをお勧めします。ローカルのプログラムではなく、Herokuにデプロイした場合はHeroku側で定期的に実行してくれるものがあるようなのでそちらもご検討してみてはいかがでしょうか。何かあればコメントでお願いします。

 

 おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

【Python】Fortniteのショップ情報を呟くTwitterのbotを作った

はじめに

 今回の記事は技術的なことになります。どのようなbotかなどは別途以下の記事をみてください。

parco1021.hatenablog.com

 また、今回は全体的なことのみ書いて具体的なことを書いたものは別途書いてこのブログへリンクを追加していこうと思っています。

使用環境

conda 4.7.10

Python 3.7.3

Twitter api

Fortnite Tracker api

処理の流れ

今回作成したbotの処理のフローが以下です。

 

f:id:Parco1021:20191119125250p:plain


この図の①~⑧までの処理を後で別々に書いていこうと思います。

プログラム

import requests
import twitter
import urllib.request
import requests
from requests_oauthlib import OAuth1Session
import json
import time
from datetime import date

key=''
URL='https://api.fortnitetracker.com/v1/store'
CK=''
CS=''
AT=''
AS=''
twitter = OAuth1Session(CK,CS,AT,AS)

url_media = "https://upload.twitter.com/1.1/media/upload.json"
url_text = "https://api.twitter.com/1.1/statuses/update.json"
   


def tweet(media_id,j,result): #⑦、⑧
    now_date = date.today()
    y = now_date.year
    m = now_date.month
    d = now_date.day
    len_pic = len(media_id)
    if len_pic == 4:
        tweet="{}年{}月{}日のショップその{}\n{} {}vBucks\n{} {}vBucks\n{} {}vBucks\n{} {}vBucks\n#Fortnite #フォートナイトショップ"
        status = tweet.format(y,m,d,j+1,result[j*4]['name'],result[j*4]['vBucks'],result[j*4+1]['name'],result[j*4+1]['vBucks'],
        result[j*4+2]['name'],result[j*4+2]['vBucks'],result[j*4+3]['name'],result[j*4+3]['vBucks'])
    elif len_pic == 3:
        tweet="{}年{}月{}日のショップその{}\n{} {}vBucks\n{} {}vBucks\n{} {}vBucks\n#Fortnite #フォートナイトショップ"
        status = tweet.format(y,m,d,j+1,result[j*4]['name'],result[j*4]['vBucks'],result[j*4+1]['name'],result[j*4+1]['vBucks'],
        result[j*4+2]['name'],result[j*4+2]['vBucks'])
    elif len_pic == 2:
        tweet="{}年{}月{}日のショップその{}\n{} {}vBucks\n{} {}vBucks\n#Fortnite #フォートナイトショップ"
        status = tweet.format(y,m,d,j+1,result[j*4]['name'],result[j*4]['vBucks'],result[j*4+1]['name'],result[j*4+1]['vBucks'])
    else:
        tweet="{}年{}月{}日のショップその{}\n{} {}vBucks\n#Fortnite #フォートナイトショップ"
        status = tweet.format(y,m,d,j+1,result[j*4]['name'],result[j*4]['vBucks'])
    media_id= ','.join(media_id)
    params = {"status": status, "media_ids": media_id}
    print(params)
    twitter.post(url_text,params=params)
    time.sleep(5)

def pic(media_id,i,j,result): #⑤、⑥
    img_url = result[i+4*j]['imageUrl']
    print(img_url)
    headers = {"User-Agent": "Mozilla/5.0"}
    request = urllib.request.Request(url=img_url,headers=headers)
    response = urllib.request.urlopen(request)
    print(response)
    data = response.read()
    files = {"media" : data}
    req_media = twitter.post(url_media,files = files)
    media_id.append(json.loads(req_media.text)['media_id_string'])
    print(media_id)
    return media_id

def call():
    #②、③、④ここから
    headers = {'TRN-Api-Key' : key}
    r = requests.get(URL, headers = headers)
    result = eval(r.text)
    print(len(result))
    print(result)
    num = len(result) // 4
    mod = len(result) % 4
#ここまで for j in range(0,num): media_id = [] name = [] vbucks = [] for i in range(4): pic(media_id,i,j,result) #⑤、⑥ tweet(media_id,j,result) #⑦、⑧ media_id = [] name = [] vbucks = [] for k in range(0,mod): pic(media_id,k,j+1,result) #⑤、⑥ tweet(media_id,j+1,result) #⑦、⑧ if __name__ =='__main__': call()

 データ数は毎日異なるのでそれに対応させるように書きます。4つずつデータを呟きたいのでまずは4枚ずつできるだけ呟きます。例えば17個のデータだったらnum=17//4=4となります。なのでまずはfor文のところのjを4回まわすことで16個呟きます。そして残りの1を呟く方法としてmod=17%4=1となるのでfor文のところのkをmodの数だけまわします。

 それぞれ値を関数に渡して処理をします。本当は呟く文章の箇所でif-elifを多用したくなかったのですがよくわからなかったのでゴリ押しました。

 

各処理の説明

①毎日定時に実行

parco1021.hatenablog.com

 

②、③呟くデータをAPIを叩いて取得する

parco1021.hatenablog.com

 

④~⑧データを呟く

parco1021.hatenablog.com

 

全体のコードはgithub見てください。コードが汚いのは許してね。

https://github.com/tnb1021/fortnitebot

 

 おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

Fortniteショップbotのトリセツ

 今回、Fortniteのショップ情報を呟くbotを作成しました。その説明を書きます。あくまでトリセツなので技術的なことについて深く書きません。技術的な話は別で書きます。情報は元はFortnite Trackerさんです。

  • 呟く内容は「商品名 値段vBucks」となっています。詳細は書きませんが、海外から情報を受け取っているので英語表記となっています。手作業で翻訳することも可能ですが、現在実施する方針はありません。
  • 商品の画像を4枚(Twitterで投稿できる最大枚数)区切りで呟いているので一日の投稿数が(商品数)÷4+1となります。画像付きなので多少ライムラインを圧迫します。
  • 情報の特性上、ある商品の別バージョンがショップに並んでいる場合、値段が0表記になる可能性があります。0vBuckとなっていた場合はそのような解釈をしていただけると幸いです。
  • Fortniteのショップは日本時間の午前9時に更新されますが、本botは9時5分の更新となります。
  • ご意見・ご要望ありましたら本記事へのコメントまたはbotへリプライしてください。しかし全て対応できるわけではないことをご容赦ください。
  • botは予告なく終了する可能性があります。

↓↓もしよろしければフォロー・いいね・RTお願いします!!

 

 おわり。

もしよければ↓ぽちっと↓お願いします。

ブログランキング・にほんブログ村へにほんブログ村

 

PVアクセスランキング にほんブログ村

スポンサーリンク