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

no CUDA-capable device is detected

rayでGPUを使用し、並列処理をしていると以下のようなエラーがでる。

RuntimeError: cuda runtime error (100) : no CUDA-capable device is detected at /opt/conda/conda-bld/pytorch_1591914855613/work/aten/src/THC/THCGeneral.cpp:47

エラーの対処方法

長らくこのエラーについて考えたが明確な答えは分からなかった。。。。

しかし、このエラーが出てもrayでGPUを使うことができる方法を見つけた。

それは実行したいプログラムに以下のコードを追加することである。

while True:
     input = input()
     #以下実行したいコード
     :
     :

上記のコードを説明する。
まず、while Trueは無限ループである。
input()は入力である。
そして、while Trueの中に実行したいコードを入れる。

タイトルのようなエラーが出たらcontrol Cで再び入力に戻す。
そしてまた、適当な入力をする。
するとエラーがなくなり実行できる。

実行し終えたら

無限ループなので
control Cを押して終了する。

本当の意味の解決方法を見つけたらまた報告する。

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

Chainerによる機械学習のためのPython学習メモ 11, 12章 Pandas Matplotlib 入門

What

Chainerを利用して機械学習を学ぶにあたり、私自身が、気がついた点、リサーチした内容をまとめる記事になります。今回は、scikit-learnを勉強します。

私の理解に基づいて記述しているため、間違っている場合があります。間違いは都度修正するつもりです、ご容赦ください。

Content

Pandas

Pandas はデータ操作によく用いられるパッケージであり、CSV などの一般的なデータ形式で保存されたデータの読み込みや、条件を指定しての一部データの抽出など、機械学習手法で取り扱うデータを整理するのに便利です。

とまぁ計算ツールみたいなものですね。算盤とか電卓とか計算を楽にしてくれる道具的な立ち位置です。ちなみに下記の操作ができるみたいです。

・CSV ファイルの読み書き
・統計量の算出
・並べ替え
・データの選択
・条件指定による選択
・欠損値の除去 / 補間
・ndarray とデータフレームを相互に変換
・グラフの描画

Matplotlib

グラフを描けるライブラリ。折れ線、散布図、箱ひげなど。特筆すべき点は特に見当たらない。

またコード書きながら抑えるポイントが出てきたら追記します

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

Chainerによる機械学習のためのPython学習メモ 11章 Pandas 入門

What

Chainerを利用して機械学習を学ぶにあたり、私自身が、気がついた点、リサーチした内容をまとめる記事になります。今回は、scikit-learnを勉強します。

私の理解に基づいて記述しているため、間違っている場合があります。間違いは都度修正するつもりです、ご容赦ください。

Content

Pandas はデータ操作によく用いられるパッケージであり、CSV などの一般的なデータ形式で保存されたデータの読み込みや、条件を指定しての一部データの抽出など、機械学習手法で取り扱うデータを整理するのに便利です。

とまぁ計算ツールみたいなものですね。算盤とか電卓とか計算を楽にしてくれる道具的な立ち位置です。ちなみに下記の操作ができるみたいです。

・CSV ファイルの読み書き
・統計量の算出
・並べ替え
・データの選択
・条件指定による選択
・欠損値の除去 / 補間
・ndarray とデータフレームを相互に変換
・グラフの描画

またコード書きながら抑えるポイントが出てきたら追記します

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

CNNと画像認識による構造の違いについて

(2021/04/27) 去年10月に書いた記事ですが、一般公開にしてなかったのですこし書き加えて公開にしました。 はじめに はじめまして。python、機械学習を学び始めてまだ一年もたっていない駆け出しのエンジニア大学生です。 自分は今アルバイトでよく深層学習による画像分析を行っています。そういった中で、自分はすでに実装されていたネットワークを使ったもで解析をしていたのですが、実際そういうネットワークはどういうものなのか、CNNやネットワークそのものについてはあまり勉強ができていませんでした。 そこで、「そもそもCNNってなんだろう?」、「手法による違いでネットワークの構造ってどう変わってくるのだろう?」 ということについて、自分自身のための勉強も兼ねて記事を書きました。 この記事は自分のようなエンジニア初心者を対象にした記事になります。記事の中で簡単なコードを挟んでいるので、実際に実行してみてCNNやその中身で使われているもの、また論文を読む際に必要な知識についての理解が深まっていただければと思います(数式はまったく使いません)。 間違いもあるかと思いますが、コメント等で指摘をお願いしますm(_ _)m 1. CNN(Convolutional Neural Network)について CNNというのは、畳み込み(Convolution)とプーリング(Pooling)という層が使われたニューラルネットワークのことを指します。CNNの層が深いネットワークのことをDeep Convolutional Neural Network(DCNN)ともいいますが、全体的にCNNと呼ぶ方が一般的です。 ここでは、CNNで使われている畳み込みとプーリングというのはどんな処理が行われているかを例を通して説明していきます。 1.1 畳み込みとは CNNで使われている畳み込み処理は画像処理で使われる空間フィルタリングとほとんど同じです。「空間フィルタリングって何?」と思う方もいるかもしれないので、まず空間フィルタリングについて説明してから畳み込み処理を解説します。 空間フィルタリング 空間フィルタリングというのは、あるサイズのカーネル(空間フィルタともいいます)と、それと同じサイズの画素との内積を取っていくことで、新しい画像データを作る(フィルタされた画像を得る)という手法です。 例えば、次のように3x3ピクセルのフィルタ係数(オレンジ)と5x5の画像データがあるとします。     このフィルタ係数の画像データの青枠で示した部分の各画素値について、同じ成分とを掛け合わせ、それらの和をとります。この操作を畳み込みといいます。 この畳み込みによって得られた値(上図では18)が新しい画素値、つまり特徴量となります。これを1マスずつスライドしていくことによって他の画素に対しても同様に行っていきます。 こうして、新しい特徴量で示された画像データができました。ただし、これで得られる画像は元の画像に対して外枠1マス分だけ小さくなってしまうので、あらかじめ外枠を1マス増やし、増やした領域を0で埋めることで変換前と後で画像サイズが変化しないようにするという方法(ゼロパディングなど)があります。 この一連の操作が空間フィルタリングです。空間フィルタリングでは、このカーネル内の係数を変えることで様々な変換が行うことができます。例として、ソーベルフィルタでは下図のような鹿の画像が中央のカーネルを用いることで右下のようにエッジが抽出された画像に変換できます: (自身の撮影した画像より) サンプルコード import cv2 # 画像の読み込み # フィルタはRGBではなくグレイスケールで読み込ませる img_base = cv2.imread('sample.png', cv2.IMREAD_GRAYSCALE) # ソーベルフィルタ(y)を適用させる sobel_img = cv2.Sobel(img_base, cv2.CV_64F, 0, 1, ksize=3) # ちなみに、x方向では次のようになる # sobel_img = cv2.Sobel(img_base, cv2.CV_64F, 1, 0, ksize=3) # 出力 cv2.imwrite('sobel_sample.png', sobel_img) 畳み込み層 本題です。前項で説明した空間フィルタリングでは、出力は1つのフィルタが施された画像になりますが、CNNでの畳み込み層では、入力から、特徴マップと呼ばれるデータが出力されます。 特徴マップは入力データの中にある特徴的な部分などの情報が入ったデータのことで、鹿の鼻や角だったり、先ほど説明したようなソーベルフィルタで得られるエッジ等の特徴が含まれています。 畳み込み層ではフィルタの数だけカーネルが用意されており、それぞれで空間フィルタリングの処理を行っていくことで、最終的に縦横のサイズ、フィルタ数だけの深さのようなデータが返されます(例えば、出力の縦横が28x28、フィルタ数が32であれば(28,28,32)の3階テンソル)。 ※実際は上のようなきれいなものが得られるわけではないので、VGG16ネットワークの各レイヤの特徴を可視化するなどを参考にしてみてください。) 学習においては、フィルタリングをする際のカーネルのパラメータ(重み行列)とバイアスが学習されます。初期値は任意の重み行列(バイアスも任意)となり、それらを学習によって調整していくことで、最適な出力が得られるようになります。 tf.kerasでは次のように実装されています。画像に対して一般的に使われるのはConv2Dというレイヤーです。時系列データなどにおいてはConv1D、3次元の空間データにはConv3Dなども使われています。 conv2d_layer = tf.keras.layers.Conv2D( filters, kernel_size, strides=1, padding='valid', activation='relu', kernel_initializer='glorot_uniform', bias_initializer='zeros') ) 引数 内容 fileters 出力されるフィルタの数 kernel_size カーネルの大きさ、int型であれば縦横が同じサイズ、tupleであれば指定サイズになります。 strides スライスするときのステップ数。1でスキップなしのスライディングになります。kernel_sizeと同様、int型であれば縦横が同じサイズ、tupleであれば指定サイズになります。 padding 'valid'でなし、'same'で外枠0埋めです。 activation 活性化関数。中間層には'relu'が、出力層は'softmax'が使われるのが一般的です。 kernel_initializer 学習前のカーネルの重み行列の初期化手法を指定します。0埋めや1埋め、乱数など初期化の手法も様々ありますが、活性化関数にreluを使う畳み込み層ではよく'he_normal'が使われています。 bias_initializer 学習前のバイアスの初期化手法を指定します。デフォルトは'zeros'(=0)です。 (他にも引数は存在しますが、話が難しくなるので省略しています。それらは公式ドキュメントを参照してください。) サンプルコード from tensorflow.keras.layers import Conv2D, Input # 28x28x3(28x28, RGB)の入力 x = Input(shape=(28,28,3)) # パディング無し x_nopadding = Conv2D(filters=32, kernel_size=3, strides=1, padding='valid')(x) print(x_nopadding.shape) # バッチサイズ(今回は入力で指定してないのでNone)が加わった4階テンソルが返される # >>(None, 26, 26, 32) # パディングあり x_padding = Conv2D(filters=32, kernel_size=3, strides=1, padding='same')(x) print(x_padding.shape) # >>(None, 28, 28, 32) 1.2 プーリングとは プーリングは畳み込みと比べて非常にわかりやすいです。簡単に言ってしまえば、「指定した領域の中から条件をみたす特徴量を取り出して、特徴マップのサイズを小さくする」になります。プーリングをまとめてダウンサンプリングともいったりします。 平均値プーリング 選択範囲の平均を取ったものを出力します。 tf.kerasではtf.keras.layers.AveragePooling2D()で実装がなされています(公式ドキュメント)。 最大値プーリング 選択範囲の最も大きい値を出力します。 tf.kerasではtf.keras.layers.MaxPooling2D()で実装がなされています(公式ドキュメント)。 プーリンの種類は上記2です。ほかにもGlobalのついたプーリング層(GlobalAveragePooling, GlobalMaxPooling)も存在しますが、それらは層のすべてが対象になるので、全結合層(後述)で使われるものになります。 このようなダウンサンプリングとは対象に、セマンティックセグメンテーション(後述)において用いられているアップサンプリング(tf.keras.Upsampling2D、公式ドキュメント)と呼ばれるものがあります。これは画像の拡大とほとんど同じです。 1.3 プーリング層の必要性 一般的なCNNのエンコーダにはInput→Conv→Pooling→Conv→Pooling→…といった畳み込みとプーリングがセットになったものが多く見られます。 「畳み込みだけで十分じゃないのか?」と僕も初めは思ったことがあったりしましたが、実際はそうは上手くいかないみたいです。 より広範囲の特徴量が得られない 例えば256x256の画像で3x3の畳み込みをすると、そのマスの中の特徴量を見るわけですが。その際得られる特徴量は非常に細かい部分を見たものになります。人間が何かを学ぶ上で小さいところから大きいところまで観察するように、機械学習においても細かい部分だけでなくより局所的な部分の特徴量も必要です。そうでないと、学習が進まなくなったりして精度が悪くなってしまいます。小さいサイズの画像を学習したりする場合はあまり気にすることがないときもありますが、その場合は次ののようなことが起こります。 学習パラメータが多すぎて過学習になる 畳み込みを繰り返すと、学習パラメータの量は増えていきます。パラメータは増えるほど様々な情報をより正確に学習できますが、それだと学習に時間がかかりすぎたり、データに対してパラメータの数が過剰で過学習や学習不足に陥ります。 2020/11/10追記 : コメントの指摘にもある通り、単に多いとダメというわけでなく、層が深いDNN(後述のセマンティックセグメンテーション等)では逆にパラメータが多い方が学習が良くなるケースが多いみたいです。ここは見なかったことにしてください… このように、畳み込みだけで構成するのは過学習や学習不足などの原因になります。ダウンサンプリングをしてから畳み込みを行うことで、より広い範囲の特徴量を取得していく必要があるわけです(ただし、畳み込みは連続して使うのはダメということではなく、プーリングを途中で入れたほうが良いということの意味であることは理解してください)。 2. 画像認識による構造の違いについて CNNはいろんな様式の分類で使われていますが、今回は画像認識の中の「クラス分類」、「物体検出」、「セマンティックセグメンテーション」について説明していきます。 2.1 クラス分類 最も有名な手法の1つです。入力の画像から、その中に何があるかを分類します。入力は画像で、出力はクラス毎の確率になります。 特徴 クラス分類においては、CNN層の出力層の直前で全結合層(Fully connected layer)が使われるのが特徴です。全結合層より前の層では、それまでの画像から得られた特徴量が含まれています、それらを1次元に畳み込むことで、「その画像には何があるか」という情報へと変換できるのです。最終的にクラス数だけのベクトルに変換することで分類の出力になります。 全結合層で使われるものとして、にtf.keras内ではFlatten、DenseまたはGlovalAveragePooling2Dなどがそれにあたります。 Flatten : n階テンソルの形状をを1次元テンソルに変換する(パラメータ数は保持) サンプルコード from tensorflow.keras.layers import Flatten, Input x = Input(shape=(256, 256, 3)) x = Flatten()(x) # 256x256x3=196,608 print(x.shape) # >>> TensorShape([None, 196608]) Dense : 1次元ベクトルのパラメータを指定したパラメータ数へ変換する。入力は1次元であることが必要 サンプルコード from tensorflow.keras.layers import Dense, Input x = Input(shape=(256*256*3, )) x = Dense()(x) print(x.shape) # >>> TensorShape([None, 256]) GlobalAveragePooling2D : n階テンソルの各ベクトルについて、各層の特徴量の平均を求めることで1次元テンソルに変換する(パラメータ数が減る) サンプルコード from tensorflow.keras.layers import GlobalAveragePooling2D, Input x = Input(shape=(256, 256, 3)) x = GlobalAveragePooling2D()(x) print(x.shape) # >>> TensorShape([None, 3]) 例 以下はmnistデータをCNNを使って分類してみた例です。 import numpy as np import tensorflow as tf from tensorflow.keras.datasets.mnist import load_data from tensorflow.keras.models import Model from tensorflow.keras.layers import * from tensorflow.keras.utils import to_categorical # mnistデータを読み込む (train_images, train_labels), (test_images, test_labels) = load_data() # 出力するクラス数 classes = 10 # バッチサイズ batch_size = len(train_images)//16 #60000//16=3750 # エポック epochs = 20 # Functional APIでの実装 # ______________________________________________________________________________________________ # 入力は(28,28,1)のグレイスケール # 学習時は(batch_size, 28, 28, 1)が入力になる。 input_layer = Input(shape=(28,28,1)) # 1つ目の畳み込み x = Conv2D(filters=32, kernel_size=3, strides=1, padding='same', activation='relu')(input_layer) # 最大値プーリング # (28, 28, 32) → (14, 14, 32) x = MaxPooling2D((2,2))(x) # 2つ目の畳み込み x = Conv2D(filters=64, kernel_size=3, strides=1, padding='same', activation='relu')(x) # 最大値プーリング # (14, 14, 64) → (7, 7, 64) x = MaxPooling2D((2,2))(x) # 3つ目の畳み込み # 出力は(7, 7, 64) x = Conv2D(filters=64, kernel_size=3, strides=1, padding='same', activation='relu')(x) # ここから全結合層。Flattenをつかって(3136, )の2次元ベクトルに変換。※7x7x64=3136 x = Flatten()(x) # Denseをつかってサイズ(64, )にする x = Dense(64, activation='relu')(x) # Denseをつかって(classes, )のサイズにする # 活性化関数にsoftmaxを使うことで、出力の各成分は区間[0, 1]で表現される output_layer = Dense(classes, activation='softmax')(x) # ______________________________________________________________________________________________ # 入力層と出力層を引数に入れてmodelを作成 model = Model(input_layer, output_layer) # データの前処理 # 学習データを[0,1]で正規化する train_images = train_images / 255.0 test_images = test_images / 255.0 # ラベルをバイナリクラスに変換する(categorical_crossentropyを使うため) train_labels = to_categorical(train_labels) test_labels = to_categorical(test_labels) # モデルのコンパイル model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 学習 model.fit(train_images, train_labels, batch_size=batch_size ,epochs=epochs) # テストデータを使って精度を確かめる test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2) print('\nTest accuracy:', test_acc) 出力: Test accuracy: 0.9883000254631042 有名なもの クラス分類で使われネットワークの例としては、おそらく機械学習を触れたならば必ず聞くであろうこの2つが有名です: VGG16 : 16層からなるDCNNネットワーク ResNet(Residual Network) : 残差ブロックをいう手法を取り入れることで勾配問題に対処した tensorflowでも上記は実装されています。 2.2 物体検出 物体検出とは、入力の画像の中から、その中に何があり、それがどこにあるかが欲しいときに使われる手法です。出力は、分類結果に加えて対象のバウンディングボックス(矩形)を表示するために必要な座標などです。 (自身の撮影した画像より) 特徴 物体検出では、上の画像のように複数の物体のクラスとその位置を出力としてできるのが特徴です(複数でなく、1つの結果とそのバウンディングボックスを検出するものをImage Classification&Localizationともいいます)。 ネットワークの構造の特徴については、すみません、もう少し勉強に時間がかかりそうなのでいつか紹介できればと思います… 参考になるリンクや記事は掲載しておきます。 物体検出についての歴史まとめ(1) 最近のSingle Shot系の物体検出のアーキテクチャまとめ 有名なもの R-CNN(Regions with Convolutional Neural Networks) Faster R-CNN YOLO(You Only Look Once) SSD(Single Shot MultiBox Detector) FPN(Feature Pyramid Networks) 2.3 セマンティックセグメンテーション セマンティックセグメンテーションとは、入力の画像から、その中に何があり、どこにあってどのような形かが必要なときに使われます。物体検出では矩形を用いて物体のクラスとその位置を識別しましたが、セマンティックセグメンテーションでは1ピクセルにつきクラス分類することでそのクラスと形状を検出し、マスク画像が出力となります。 (A Unified Architecture for Instance and Semantic Segmentationより引用) 特徴 構造については、一番わかりやすいUNetで紹介します。 (論文U-Net: Convolutional Networks for Biomedical Image Segmentation Fig.1より少し編集) UNetは、畳み込みとプーリングを繰り返すエンコーダと、アップサンプリングを繰り返すことで元のサイズに戻すencoder-decoderネットワークの構造をしています。 エンコーダの部分は、クラス分類で見られた全結合層を畳み込み層に置き換えたFully Convolutional Network(FCN)という構造を採用しています。出力層で全結合によって1次元にするのではなく、畳み込みを行うことで「物体が何か」という情報から「どこにあるのか」という特徴量が得られるようになるわけです。 デコーダについては、小さくなったマップサイズをUpsamplingという解像度を上げる層と畳み込み層を使うことで元の入力画像サイズへと拡大していきます。 図中の中央の灰色の矢印ですが、これはSkip Connectionと呼ばれるものです。エンコーダでのダウンサンプリング前に得られた特徴量を、デコーダの得られた特徴量とつなげることによって、より精度の高い出力結果が得られるようになります。加えて、層を深くすることで発生しやすくなる過学習などを抑えるなどの効果もあります。 有名なもの UNet PSPNet(Pyramid Scene Parsing Network) : Pyramid Poolingモジュールという手法が特徴 P-FPN(Panoptic FPN) など… これらはsegmentation_modelsですでに実装がされています(サンプルコード参照) サンプルコード # おまじない import os os.environ['SM_FRAMEWORK'] = 'tf.keras' # segmentation_modelsをインポート import segmentation_models as sm # 入力サイズ input_size = (480, 480, 3) # PSPNet pspnet_model = sm.PSPNet( backbone_name='resnet50', # バックボーンの構造(エンコーダの構造)は何を使うか input_shape=input_size, #入力サイズ(縦横は48の倍数であること) encoder_weights="imagenet", # バックボーンの重みは何を使うか # あらかじめ学習済みの重みを使うことで、学習の精度が上げられる classes=3, # 出力される画像内に含まれるラベルの数。つまり(480, 480, classes)のような出力になる activation='softmax') # 出力層の活性化関数。sigmoidかsoftmax # UNet unet_model = sm.Unet( backbone_name='resnet50', input_shape=input_size, #入力サイズ(縦横は48の倍数であること) encoder_weights="imagenet", classes=3, activation='sigmoid') # FPN fpn_model = sm.FPN( backbone_name='resnet50', input_shape=input_size, #入力サイズ(縦横は32の倍数であること) encoder_weights="imagenet", classes=3, activation='softmax') 3. おわり 長い記事になってしまいましたが、参考になっていただければ幸いです。バッチ正規化、最適化のための損失関数やオプティマイザについては触れていなかったので、機会があれば書いてみたいです。 → オプティマイザ、損失関数について記事にしました (学習最適化のための損失関数とOptimizer & MRI画像を使った比較) 畳み込み層については、もしかしたら今回のでは十分理解できないかもしれません。そうであった場合は、Kerasライブラリの作者であるFrancois Chollet氏によって書かれたPythonとKerasによるディープラーニング本を手に取ってみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonanywhereにuploadしたDjangoのSQLite3をGUIだけで変更する方法

pythonanywhereにuploadしたDjangoのデータを変更する方法。
コマンドラインを使用せずに、直観的に変更方法の備忘録。

やりたかったこと
・しばらく放置していた自身のポートフォリオサイトのアプリのリンク先と画像を久しぶりに更新したかった。

変更前の画像

ステップ1
pythonanywhereのコンソール画面を開いて、sqllite3ファイルが置いてあるフォルダまで進む。sqlite3ファイルをダウンロードする。

スクリーンショット 2020-10-20 205239.png

ステップ2
DB Browser(sqlite) を使用して、ダウンロードしたdb.sqlite3ファイルを開く。

sql3broser.png

ステップ3
リンク先と新たな画像名を変更する。変更後、Applyを押す。

editdatabase.png

ステップ4
新しい画像をアップロードする。
pythonanywhereのコンソール画面から、写真の置いてある(staticの設定してある)フォルダまで移動する。
新しい画像をuploadする。
(ステップ3で変更した画像名と一致するようにする。)

upload_pic.png

ステップ5

再度、sqlite3.dbファイルがあるフォルダに移動し、ローカルで書き換えたsqlite3.dbをアップロードして置き換える。

変更後.png

以上で修正完了。

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

OpenCVで画像切り取りツール作成、射影変換の注意点(少しだけ)

はじめに

OpenCVを使用して画像切り取りツールを作成しました。
対象物を切り取り、射影変換して形を整えた後それぞれを保存します。

対象物は以下のような画像にあるフィルムを想定しています。
これでなくても、四角形を抽出する際は使用できると思います。

sample.jpg       sample2.png

また、作成中に射影変換で躓いたので最後に内容、解決策を記載しています。
詳細

環境

Mac OS
python 3.8.5

opencv-python 4.4.0.44
numpy 1.19.2
tqdm 4.50.2

pip install opencv-python
pip install tqdm

プログレスバーを使用する為tqdmをインポートしています。

射影変換

対象物を真正面から撮影したように補正する処理です。
切り取りした画像を直角にしたかったので使用しました。

射影変換後.jpeg

射影変換の参考記事
Python/OpenCVの射影変換なら簡単に画像補正ができる! | WATLAB -Python, 信号処理, AI-
OpenCVを使って画像の射影変換をしてみるwithPython - Qiita

全文

画像読み取りに日本語パスも対応させるため以下の記事を参考にしています。
Python OpenCV の cv2.imread 及び cv2.imwrite で日本語を含むファイルパスを取り扱う際の問題への対処について - Qiita

操作方法は
1. スクリプトと同じ階層にresourceフォルダ作成
2. resourceフォルダ内に処理したい画像をいれる(複数可, jpg, jpeg or png)
3. 実行

結果は"./resultフォルダ/画像ファイル名/画像ファイル名_0,..."と保存されていきます。
ファイル名と同じフォルダがresultに存在していた場合処理をスルーします。

うまく切り取りできない場合はthresh_valueかminimum_areaを変更してみて下さい.
画像のしきい値処理 — OpenCV-Python Tutorials 1 documentation
領域(輪郭)の特徴 — OpenCV-Python Tutorials 1 documentation

import os, shutil, time
from pathlib import Path
import cv2
import numpy as np
from tqdm import tqdm

thresh_value = 240  # 2値化する時の境界値, これより画素値が小さければ白にする(max:255)
minimum_area = 10000  # 輪郭を取得したときにこれより面積が小さい物は処理しない(対象物以外のドットを検出してしまったとき用)


def imread(filename, flags=cv2.IMREAD_COLOR, dtype=np.uint8):
    try:
        n = np.fromfile(filename, dtype)
        img = cv2.imdecode(n, flags)
        return img
    except Exception as e:
        print(e)
        return None


def imwrite(filename, img, params=None):
    try:
        ext = os.path.splitext(filename)[1]
        result, n = cv2.imencode(ext, img, params)

        if result:
            with open(filename, mode='w+b') as f:
                n.tofile(f)
            return True
        else:
            return False
    except Exception as e:
        print(e)
        return False


def calculate_width_height(pts, add):
    """
    検出した形状の幅, 高さを三平方を使用して求める
    あまりに斜めになっていると射影変換したときに形状が変わる為

    :parameter
    ------------
    pts: numpy.ndarray
        抽出した形状の4点の座標, shape=(4, 1, 2)
    add: int
        形状によって始点の座標が違う為その補正

    :return
    ------------
    width: int
        幅の計算値
    height: int
        高さの計算値
    """
    top_left_cood = pts[0 + add][0]
    bottom_left_cood = pts[1 + add][0]
    bottom_right_cood = pts[2 + add][0]

    width = np.int(np.linalg.norm(bottom_left_cood - bottom_right_cood))
    height = np.int(np.linalg.norm(top_left_cood - bottom_left_cood))

    return width, height


def img_cut():
    """
    resourceフォルダにある画像(jpg, png)を読み取り対象物の輪郭を取得
    対象物を切り取り射影変換して正面に持ってくる

    1. フォルダ, ファイル読込
    2. 画像読込, 2値化(白黒)処理
    3. 輪郭取得
    4. 射影変換
    5. 出力
    6. resourceファイルをresultへ移動

    :return: None
    """

    # 1. フォルダ, ファイル読込
    resource_folder = Path(r'./resource')
    result_folder = Path(r'./result')
    # resultフォルダが存在していなかったら作成
    if not result_folder.exists():
        result_folder.mkdir()

    img_list1 = list(resource_folder.glob('*.jpg'))  # フォルダ内にあるjpgファイルのpathリスト
    img_list2 = list(resource_folder.glob('*.jpeg'))
    img_list3 = list(resource_folder.glob('*.png'))
    img_list = img_list1 + img_list2 + img_list3

    for img in img_list:
        img_name, img_suffix = img.stem, img.suffix  # 画像の名前と拡張子取得

        # resultフォルダ内に画像ファイル名でフォルダを作成, 既に同じフォルダが存在している場合変換をスキップ
        result_img_folder = Path(r'./result/{}'.format(img_name))
        if not result_img_folder.exists():
            result_img_folder.mkdir()
        else:
            print('{}と同じ名前のフォルダがresult内に存在している為変換できません'.format(img_name))
            continue

        # 2. 画像読込, 2値化(白黒)処理
        read_img = imread(str(img))
        gray_img = cv2.cvtColor(read_img, cv2.COLOR_BGR2GRAY)
        ret, thresh_img = cv2.threshold(gray_img, thresh_value, 255, cv2.THRESH_BINARY_INV)

        # --------------------------------------------
        # 2値化画像確認用
        # cv2.namedWindow('final', cv2.WINDOW_NORMAL)
        # cv2.imshow('final', thresh_img)
        # cv2.waitKey(0)
        # cv2.destroyAllWindows()
        # --------------------------------------------

        # 3. 輪郭取得
        # cv2.RETR_EXTERNAL:検出した輪郭のうち、最も外側にある輪郭だけを抽出->輪郭の中に輪郭があってもそれを無視する
        # cv2.CHAIN_APPROX_SIMPLE:輪郭の辺ではなく、角4点のみ取得
        contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        process_cnt = []  # 実際に切り取りする輪郭のリスト
        for cnt in contours:
            if cv2.contourArea(cnt) < minimum_area:  # 輪郭内の面積が小さすぎる物は切り取らない
                continue
            process_cnt.append(cnt)

        num = 0
        for p_cnt in tqdm(process_cnt[::-1], desc='{}'.format(img_name)):  # 何故か一番下の画像から処理を始めるのでスライスで逆(上)からに直す
            x, y, w, h = cv2.boundingRect(p_cnt)  # 輪郭の左上x, y座標 & 幅, 高さ取得
            img_half_width = x + w / 2

            # cv2.arcLength: 輪郭の周囲長, Trueは輪郭が閉じているという意味
            # cv2.approPolyDP: 検出した形状の近似
            epsilon = 0.1 * cv2.arcLength(p_cnt, True)
            approx = cv2.approxPolyDP(p_cnt, epsilon, True)
            try:
                # 4. 射影変換
                pts1 = np.float32(approx)
                if pts1[0][0][0] < img_half_width:  # ptsに格納されてある座標の始点が左上だったら
                    width, height = calculate_width_height(pts1, 0)
                    pts2 = np.float32([[0, 0], [0, height], [width, height], [width, 0]])
                else:
                    width, height = calculate_width_height(pts1, 1)
                    pts2 = np.float32([[width, 0], [0, 0], [0, height], [width, height]])
            except IndexError:
                continue
            M = cv2.getPerspectiveTransform(pts1, pts2)
            dst = cv2.warpPerspective(read_img, M, (width, height))

            result_img_name = img_name + '_{}.{}'.format(num, img_suffix)
            imwrite(str(result_img_folder) + '/' + result_img_name, dst)

            num += 1
        # 6. resourceファイルをresultへ移動
        shutil.move(str(img), result_img_folder)


if __name__ == '__main__':
    img_cut()
    print('実行終了')
    time.sleep(3)

詳細

# cv2.arcLength: 輪郭の周囲長, Trueは輪郭が閉じているという意味
# cv2.approPolyDP: 検出した形状の近似
epsilon = 0.1 * cv2.arcLength(p_cnt, True)
approx = cv2.approxPolyDP(p_cnt, epsilon, True)
try:
    # 4. 射影変換
    pts1 = np.float32(approx)

pts1に対象物の角4点の座標情報が格納されています。
ex)
[[[6181. 598.]]

[[ 145. 656.]]

[[ 135. 3499.]]

[[6210. 3363.]]]

この4点を画像の角に持ってくることで正面から見たような画像に補正しています。

if pts1[0][0][0] < img_half_width:  # ptsに格納されてある座標の始点が左上だったら
    width, height = calculate_width_height(pts1, 0)
    pts2 = np.float32([[0, 0], [0, height], [width, height], [width, 0]])
else:
    width, height = calculate_width_height(pts1, 1)
    pts2 = np.float32([[width, 0], [0, 0], [0, height], [width, height]])

pts1の始点がどこかを判定しています。
格納されている座標4点は最も上にある(y軸が小さい)点を始点に反時計回りに格納されている為、画像の傾きによってpts1の始点が変わります。それを判定して射影変換後の座標pts2と対応させています。
pts座標.png

判定方法は始点のx座標が画像中央より左右どちらにいるかで判断しています。

また、対象物の形状はなるべく保持したいため、三平方を使用して幅、高さを計算、そのサイズで切り取り画像を出力しました。

終わりに

pts1の始点が変わることを知らず、最初は意味不明な画像を出力していました。ネットで探してもなかなか見つからず、最後は画像と睨めっこしてようやく分かったという状態でした。
同じように困った方がいた時に参考になれば幸いです。

参考サイト

チュートリアル
画像のしきい値処理 — OpenCV-Python Tutorials 1 documentation
輪郭: 初めの一歩 — OpenCV-Python Tutorials 1 documentation
領域(輪郭)の特徴 — OpenCV-Python Tutorials 1 documentation

OpenCV参考(輪郭, 画像切り取り, 射影変換)
OpenCVで画像から輪郭検出の基本(findContours関数あたり) | 北館テック.com
輪郭を使用して画像内のオブジェクトを取得およびトリミングする - python、image、opencv-contour
OpenCVを使って画像の射影変換をしてみるwithPython - Qiita

OpenCV 日本語パス読書対応
Python OpenCV の cv2.imread 及び cv2.imwrite で日本語を含むファイルパスを取り扱う際の問題への対処について - Qiita

numpy 三平方計算参考
ユークリッド距離を求める

プログレスバー参考
tqdmでプログレスバーを表示させる - Qiita

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

行列から探してみよう

こんばんは('Д')
いつも応援有難うございます m(_ _)m

アルゴリズムを色々書いてきましたが、
ちょっと小休止で行列で遊んでみたくなりました。
簡単な奴から行きます。

test.py
import numpy as np
Tarray = np.arange(12)
print(Tarray)
実行結果.py
[ 0  1  2  3  4  5  6  7  8  9 10 11]

これって、例えば 3行4列に変更できないのでしょうか?
はい、出来ます。

test.py
import numpy as np
Tarray = np.arange(12).reshape(3,4)
print(Tarray)
実行結果.py
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

なるほど~、面白い!!
ここから、特定の要素のみを print するには
どうすれば良いでしょうか?
こんな記述はどうでしょう。。

test.py
import numpy as np

Tarray = np.arange(12).reshape(3,4)

print(Tarray)
print()
print(f"Tarray[0,0] is {Tarray[0,0]}")
実行結果.py
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

Tarray[0,0] is 0

なるほど。( ´ー`)y-~~
ここまで分かったら、
行列から、欲しい数字を探せるかも。
やってみましょう(≧▽≦)

例えば 2行10列の 1 行目からデータを
抜き取る場合、こんな記述になりませんか?

test.py
import numpy as np

Tarray = np.arange(20).reshape(2,10)
print(Tarray)
num = int(input(": "))

for i in range(10):
    if Tarray[0,i] == num:
        print(f"founded data is placed Tarray[0,{i}] ")
実行結果.py
[[ 0  1  2  3  4  5  6  7  8  9]
 [10 11 12 13 14 15 16 17 18 19]]

: 6
founded data is placed Tarray[0,6] 

次は行をまたいで検索しましょう。
前述の Tarray[0,i] の 0 を変数に変えれば OK です。

test.py
import numpy as np

Tarray = np.arange(20).reshape(2,10)
print(Tarray)
num = int(input(": "))

for j in range(2):
    for i in range(10):
        if Tarray[j,i] == num:
            print(f"founded data is placed Tarray[{j},{i}] ")
実行結果.py
[[ 0  1  2  3  4  5  6  7  8  9]
 [10 11 12 13 14 15 16 17 18 19]]

: 13
founded data is placed Tarray[1,3] 

ではでは、行、列すらも変数になったらどうなるでしょうか。
私はこんな風に書いてみました。

matrix_search.py
import numpy as np

M,N = map(int,input("enter M , N: ").split())
atest = np.arange(M*N).reshape(M,N)
print(atest)

num = int(input("find value: "))

for i in range(M):
    for j in range(N):
        if atest[i,j] == num:
            print(f"found value is placed at [{i},{j}]")
実行結果.py
enter M , N: 4 8
[[ 0  1  2  3  4  5  6  7]
 [ 8  9 10 11 12 13 14 15]
 [16 17 18 19 20 21 22 23]
 [24 25 26 27 28 29 30 31]]

find value: 20
found value is placed at [2,4]

位置表示が 0 行 0 列があるものとして構成しています。
無いですよね、そんなの(笑)

matrix_search.py
import numpy as np

M,N = map(int,input("enter M , N: ").split())
atest = np.arange(M*N).reshape(M,N)
print(atest)

num = int(input("find value: "))

for i in range(M):
    for j in range(N):
        if atest[i,j] == num:
            print(f"found value is placed at [{i+1},{j+1}]")
実行結果.py
enter M , N: 4 8
[[ 0  1  2  3  4  5  6  7]
 [ 8  9 10 11 12 13 14 15]
 [16 17 18 19 20 21 22 23]
 [24 25 26 27 28 29 30 31]]

find value: 20
found value is placed at [3,5]

はい、今度は大丈夫そうです。
ではまた ^^) _旦~~

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

AWS lambdaを使ってニュースをスクレイピングし、更新分を定期的にLINEに通知してみる【python】

はじめに

今回が初投稿になります。プログラミングを独学で勉強しはじめて半年強ほどの大学生ですが、学んだことを少しずつアウトプットしてみたいと考え、投稿させていただきます。
コードの書き方等雑であったり汚い点が多々あると思いますので、遠慮なく指摘していただいて構いません。

目標・手順

タイトルにある通り、AWSのlambdaというツールを使って(Yahooニュースの記事)を定期的(6時間毎)にスクレイピングして、更新された記事について、LINEmessagingAPIを使って自分のLINEアカウントに通知することを目標とします。開発環境はlambdaとの連携を考慮し、cloud9を使います。

手順についてですが、
①PythonのBeautifulSoupモジュールを用いてニュースサイトから記事のタイトルとurlを20件取得、urlはAWSのS3に置いたcsvファイルに書き込む。(書き込む部分はプログラムの最後に行います。)
②前回実行分のurl(S3からcsvを読み込む)と今回取得したurlを比較して、更新分を求める。
③更新分の記事のタイトルとurlをLINEmessagingAPIを用いて通知する。
という順序になります。

LINEmessagingAPIについては、LINE BOT で ウェブページの更新監視する
BeautifulSoupでのスクレイピングは、BeautifulSoupを使ったWebスクレイピング
S3のcsvfileの読み込み、書き込みについては、AWS S3のCSVファイルをシンプルに読み込むPythonコードWrite a Pandas dataframe to CSV on S3あたりの記事を参考にさせていただきました。

コード

import urllib.request
from bs4 import BeautifulSoup
import csv
import pandas as pd
import io
import boto3
import s3fs
import itertools
from linebot import LineBotApi
from linebot.models import TextSendMessage

def lambda_handler(event, context):

    url = 'https://follow.yahoo.co.jp/themes/051839da5a7baa353480'
    html = urllib.request.urlopen(url)
    # htmlパース
    soup = BeautifulSoup(html, "html.parser")


    def news_scraping(soup=soup):
        """
        記事のタイトルとurlを取得
        """
        title_list = []
        titles = soup.select('#wrapper > section.content > ul > li:nth-child(n) > a.detailBody__wrap > div.detailBody__cnt > p.detailBody__ttl')

        for title in titles:
            title_list.append(title.string)

        url_list = []   
        urls = soup.select('#wrapper > section.content > ul > li:nth-child(n) > a.detailBody__wrap')

        for url in urls:
            url_list.append(url.get('href'))

        return title_list,url_list

    def get_s3file(bucket_name, key):
        """
        S3からcsvを読み取る
        """
        s3 = boto3.resource('s3')
        s3obj = s3.Object(bucket_name, key).get()

        return io.TextIOWrapper(io.BytesIO(s3obj['Body'].read()))

    def write_df_to_s3(csv_list):
        """
        S3に書き込む
        """
        csv_buffer = io.StringIO()
        csv_list.to_csv(csv_buffer,index=False,encoding='utf-8-sig')
        s3_resource = boto3.resource('s3')
        s3_resource.Object('バケット名','ファイル名').put(Body=csv_buffer.getvalue())

    def send_line(content):
        access_token = ********
        #Channel access token を記入
        line_bot_api = LineBotApi(access_token)
        line_bot_api.broadcast(TextSendMessage(text=content))

    ex_csv =[]
    #前回スクレイピング分のurlを入れる
    for rec in csv.reader(get_s3file('バケット名', 'ファイル名')):
        ex_csv.append(rec)

    ex_csv = ex_csv[1:]
    #index=Falseとして書き込んだはずだが読み込んだcsvの先頭に0というインデックス(?)が書き込まれていた
    ex_csv = list(itertools.chain.from_iterable(ex_csv))
    #読み込んだcsvが二次元配列になっていたため一次元に変換

    title,url = news_scraping()
    #スクレイピング実行
    csv_list = url

    #ex_csvと比較して更新分を取り出していく
    for i in range(20):
        if csv_list[i] in ex_csv[0]:
        #完全一致してくれなかったためinを使用
            num = i
        #ex_csvの先頭にある記事がcsv_listの何番目の記事に当たるか調べる
            break
        else:
            num = 'all'

    if num == 'all':
        send_list = [None]*2*20
        send_list[::2] = title
        send_list[1::2] = url
        send_list = "\n".join(send_list)
    #タイトル、urlを交互に挿入し、改行

    elif num == 0:
        send_list = '新しいニュースはありません'

    else:
        send_list = [None]*2*num
        send_list[::2] = title[:num]
        send_list[1::2] = url[:num]
        send_list = "\n".join(send_list)
    ##タイトル、urlを交互に挿入し、改行

    send_line(send_list)

    csv_list = pd.DataFrame(csv_list)
    #リストのままS3に書き込むとエラーが出るためデータ型を変換
    write_df_to_s3(csv_list)
    #S3にcsv_listを書き込んで終了

コードは以上です。(numからsend_listを作るところは関数を定義した方がよかったですね)
後はremoteにデプロイして、Amazon CloudWatch Eventsで定期実行の設定をしてあげます。特定の時間毎に実行するにはCron式でスケジューリングするのがいいかと思います。
S3へのアクセスはIAMでアクセス権限を付与する必要があるので注意する必要がありますね。

おわりに

S3のcsvの読み取り書き込みはなかなか思い通りにいかなかったです。特に書き込む際に二次元配列になってしまったところなど改善する余地がありそうです。

実は以前にselniumやheadless-chromeとlambdaでのスクレイピングを実践したことがあった(lambdaを初めて使用したことに加え、chromebinary関連で山ほどエラーが出て苦労しました。)
ため、今回は比較的短時間でコードを書くことが出来ました。
とはいえ、ローカル内のスクレイピングと比べるとかなり作業量が多くなりますね。lambdaの使い方はここでは省いていますが、かなりややこしいので他の記事を参照してみてください。

最近はdjangoやTwitterAPIなどに手を出しているので、このあたりで何か気づいた点があればまた投稿しようと考えています。

ありがとうございました。

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

Python 簡単に使える関数

最近、Pythonの学習を進めています。

まだまだ初歩の学習中ですが、覚えた関数が増えてきたので備忘録としてまとめます。

組み込み関数

組み込み関数 = 最初からPythonの機能として使える関数

下記のリストに対していくつかの組み込み関数を使っていきます。

nums = [100, 200, 500, 1000, 500]

sum() → 合計した数字を返す

print(sum(nums))
→ 2300

max() → 最大値を返す

print(max(nums))
 1000

min() → 最小値を返す

print(min(nums))
 100

len() → 要素数(文字数)を返す

print(len(nums))
 5

str() → 数字を文字列に変換

print(str(nums))
 [100, 200, 500, 1000, 500]  #文字列として出力される

インポートしたモジュールの関数

モジュール = Pythonの定義や文が入ったファイル

モジュールをインポートすることで、定義されている関数を使うことができます。

例として random モジュールを利用します。

import random

nums = [199, 288, 56, 82, 99, 1, 538, 499]

randint(n, m) → 指定した範囲(n~m)の整数をランダムに返す

print(random.randint(1, 100))
 17

choice() → 指定したリスト内の要素1つをランダムに返す

random.choice(nums)
 288

shuffle() → 指定したリストの順番をランダムに返す

random.shuffle(nums)
 [56, 499, 538, 199, 99, 288, 1, 82]

関数の定義

def 文で定義します。

例として平均を求める関数を自作します。
(平均値はstatisticsモジュールの mean() で求められますが。)

def average(nums):
    return sum(nums) / len(nums)

nums = [10, 100, 30, 43, 57, 34, 90, 76]

result = average(nums)
print(result)
 55.0

インデントされている部分のみ、関数の処理となります。

参考

https://docs.python.org/ja/3/library/functions.html
https://docs.python.org/ja/3/library/numeric.html
https://docs.python.org/ja/3/library/random.html

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

GANの出力を1枚ずつ保存する 〜PyTorchによるGANの実装とともに〜

 卒業研究でGANを扱う中で、GANの生成する画像を1枚ずつ保存する必要が出てきました。
しかし、調べても調べてもGANの実装をしている記事はどれもこんな感じの出力ばかり…
MNIST_GAN.png

 このような複数枚まとめての出力ではなく、これらが1枚ずつ出力されるようにしました。
備忘録も兼ねて記しておきます。

目的

GANの実装を行う&GANの生成画像を1枚ずつ保存する

GAN

 GAN(Generative Adversarial Network):敵対的生成ネットワークはIan J. Goodfellow氏が提案した生成モデルです。
Generative Adversarial Nets

GANの基本構造はこんな感じ
GAN_Architecture_Resize.png

 2つのネットワークを有していて、お互いに競い合いながら学習を進めていきます。
Generator:生成器Discriminator:判別器を騙せるような画像を生成し、Discriminatorは本物の画像か偽物の画像かを判別していきます。
 GANを一躍有名にしたDCGANや驚くほどリアルな画像を生成するStyleGANなど様々なアーキテクチャが提案されています。

GANの実装

 それではGANの実装に移っていきます。今回は先ほどのDCGANの実装を行っていきます。
実装の参考にしたコードはこちら

実行環境

Google Colaboratory

import&ディレクトリ作成

import argparse
import os
import numpy as np

import torchvision.transforms as transforms
from torchvision.utils import save_image

from torch.utils.data import DataLoader
from torchvision import datasets

import torch.nn as nn
import torch

os.makedirs("./images", exist_ok=True)

 必要なモジュールをインポートしていきます。今回はPyTorchで実装を行っていきます。
 GANの出力画像を保存するディレクトリも作成します。exist_ok=Trueとなっているので、既にディレクトリが存在する場合はスルーされます。

コマンドライン引数&デフォルト値の設定

 コマンドラインでepoch数やバッチサイズなどの値を指定できるようにします。同時にデフォルトの値も設定します。
 epoch数やバッチサイズなどについてはこちらの記事がわかりやすいと思います。

parser = argparse.ArgumentParser()
parser.add_argument("--n_epochs", type=int, default=200, help="number of epochs of training")
parser.add_argument("--batch_size", type=int, default=64, help="size of the batches")
parser.add_argument("--lr", type=float, default=0.0002, help="adam: learning rate")
parser.add_argument("--b1", type=float, default=0.5, help="adam: decay of first order momentum of gradient")
parser.add_argument("--b2", type=float, default=0.999, help="adam: decay of first order momentum of gradient")
parser.add_argument("--n_cpu", type=int, default=8, help="number of cpu threads to use during batch generation")
parser.add_argument("--latent_dim", type=int, default=100, help="dimensionality of the latent space")
parser.add_argument("--img_size", type=int, default=32, help="size of each image dimension")
parser.add_argument("--channels", type=int, default=1, help="number of image channels")
parser.add_argument("--sample_interval", type=int, default=400, help="interval between image sampling")
opt = parser.parse_args()
print(opt)

 コマンドラインを使用できる環境ならこのままでいいのですが、Google Colaboratoryで実装を行う場合は以下のようなエラーが発生してしまいます。

usage: ipykernel_launcher.py [-h] [--n_epochs N_EPOCHS]
                             [--batch_size BATCH_SIZE] [--lr LR] [--b1 B1]
                             [--b2 B2] [--n_cpu N_CPU]
                             [--latent_dim LATENT_DIM] [--img_size IMG_SIZE]
                             [--channels CHANNELS]
                             [--sample_interval SAMPLE_INTERVAL]
ipykernel_launcher.py: error: unrecognized arguments: -f /root/.local/share/jupyter/runtime/kernel-ecf689bc-740f-4dea-8913-e0d8ac0b1761.json
An exception has occurred, use %tb to see the full traceback.

SystemExit: 2
/usr/local/lib/python3.6/dist-packages/IPython/core/interactiveshell.py:2890: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
  warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)

 Google Colabではopt = parser.parse_args()の行をopt = parser.parse_args(args=[])としてあげると無事通ります。

CUDAの設定と重みの初期化

cuda = True if torch.cuda.is_available() else False


def weights_init_normal(m):
    classname = m.__class__.__name__
    if classname.find("Conv") != -1:
        torch.nn.init.normal_(m.weight.data, 0.0, 0.02)
    elif classname.find("BatchNorm2d") != -1:
        torch.nn.init.normal_(m.weight.data, 1.0, 0.02)
        torch.nn.init.constant_(m.bias.data, 0.0)

 GPUを使用しないと学習に相当時間かかってしまうのでCUDA(GPU)を使用できるようにします。Google Colabではランタイムの設定をGPUに変更するのを忘れないでください。

Generator

 Geberator:生成器のネットワークを定義します。

class Generator(nn.Module):
    def __init__(self):
        super(Generator, self).__init__()

        self.init_size = opt.img_size // 4
        self.l1 = nn.Sequential(nn.Linear(opt.latent_dim, 128 * self.init_size ** 2))

        self.conv_blocks = nn.Sequential(
            nn.BatchNorm2d(128),
            nn.Upsample(scale_factor=2),
            nn.Conv2d(128, 128, 3, stride=1, padding=1),
            nn.BatchNorm2d(128, 0.8),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Upsample(scale_factor=2),
            nn.Conv2d(128, 64, 3, stride=1, padding=1),
            nn.BatchNorm2d(64, 0.8),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Conv2d(64, opt.channels, 3, stride=1, padding=1),
            nn.Tanh(),
        )

    def forward(self, z):
        out = self.l1(z)
        out = out.view(out.shape[0], 128, self.init_size, self.init_size)
        img = self.conv_blocks(out)
        return img

Discriminator

 Discriminator:判別器のネットワークを定義します。

class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()

        def discriminator_block(in_filters, out_filters, bn=True):
            block = [nn.Conv2d(in_filters, out_filters, 3, 2, 1), nn.LeakyReLU(0.2, inplace=True), nn.Dropout2d(0.25)]
            if bn:
                block.append(nn.BatchNorm2d(out_filters, 0.8))
            return block

        self.model = nn.Sequential(
            *discriminator_block(opt.channels, 16, bn=False),
            *discriminator_block(16, 32),
            *discriminator_block(32, 64),
            *discriminator_block(64, 128),
        )

        # The height and width of downsampled image
        ds_size = opt.img_size // 2 ** 4
        self.adv_layer = nn.Sequential(nn.Linear(128 * ds_size ** 2, 1), nn.Sigmoid())

    def forward(self, img):
        out = self.model(img)
        out = out.view(out.shape[0], -1)
        validity = self.adv_layer(out)

        return validity

損失関数の設定とネットワーク周りの設定

# Loss function
adversarial_loss = torch.nn.BCELoss()

# Initialize generator and discriminator
generator = Generator()
discriminator = Discriminator()

if cuda:
    generator.cuda()
    discriminator.cuda()
    adversarial_loss.cuda()

# Initialize weights
generator.apply(weights_init_normal)
discriminator.apply(weights_init_normal)

# Optimizers
optimizer_G = torch.optim.Adam(generator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))

Tensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor

DataLoaderの作成

 DataLoaderを作成していきます。今回はMNISTデータセットを用いて画像生成を行います。
MNIST:手書き数字の画像データセット

# Configure data loader
os.makedirs("./data/mnist", exist_ok=True)
dataloader = torch.utils.data.DataLoader(
    datasets.MNIST("./data/mnist",train=True,download=True,
        transform=transforms.Compose([
            transforms.Resize(opt.img_size), transforms.ToTensor(), transforms.Normalize([0.5], [0.5])]
        ),
    ),batch_size=opt.batch_size,shuffle=True,
)

Training

 いざGANのTrainingを行っていきます。

# ----------
#  Training
# ----------

for epoch in range(opt.n_epochs):
    for i, (imgs, _) in enumerate(dataloader):

        # Adversarial ground truths
        valid = Tensor(imgs.shape[0], 1).fill_(1.0)
        fake = Tensor(imgs.shape[0], 1).fill_(0.0)

        # Configure input
        real_imgs = imgs.type(Tensor)

        # -----------------
        #  Train Generator
        # -----------------

        optimizer_G.zero_grad()

        # Sample noise as generator input
        z = Tensor(np.random.normal(0, 1, (imgs.shape[0], opt.latent_dim)))

        # Generate a batch of images
        gen_imgs = generator(z)

        # Loss measures generator's ability to fool the discriminator
        g_loss = adversarial_loss(discriminator(gen_imgs), valid)

        g_loss.backward()
        optimizer_G.step()

        # ---------------------
        #  Train Discriminator
        # ---------------------

        optimizer_D.zero_grad()

        # Measure discriminator's ability to classify real from generated samples
        real_loss = adversarial_loss(discriminator(real_imgs), valid)
        fake_loss = adversarial_loss(discriminator(gen_imgs.detach()), fake)
        d_loss = (real_loss + fake_loss) / 2

        d_loss.backward()
        optimizer_D.step()

        print(
            "[Epoch %d/%d] [Batch %d/%d] [D loss: %f] [G loss: %f]"
            % (epoch, opt.n_epochs, i, len(dataloader), d_loss.item(), g_loss.item())
        )

        batches_done = epoch * len(dataloader) + i
        if batches_done % opt.sample_interval == 0:
            save_image(gen_imgs.data[:25], "images/%d.png" % batches_done, nrow=5, normalize=True)

実行結果

 一定間隔で結果が保存されるので実行結果をGIF画像で見ていきます。
dcgan.gif

人が見てもきちんとわかる数字たちが生成されています。

画像を1枚ずつ保存したい

 こんな人なかなかいないと思うのですが、調べてもなかなか出てこなかったので共有します。
上記のTrainingの部分にあった

if batches_done % opt.sample_interval == 0:
    save_image(gen_imgs.data[:25], "images/%d.png" % batches_done, nrow=5, normalize=True)

この部分を以下のように変更すれば1枚ずつ保存できます。

if batches_done % opt.sample_interval == 0:
     save_gen_img = gen_img[0]
     save_image(save_gen_imgs, "images/%d.png" % batches_done, normalize=True)

 1枚ずつ複数枚保存自体場合はfor文でも使って欲しい枚数文save_imageを繰り返せば大丈夫だと思います。訓練時間はグッと増えますが
これで最初の目的であったGANの出力を1枚づつ保存することを達成しました。

まとめ

 今回はPyTorchでDCGANの実装を行うとともに、GANの出力を1枚ずつ保存できるようにし、実際に手書きの数字が生成されていることを確認できました。
 次はGANの出力を制御できるconditional GAN(cGAN)について書いていこうと思います。cGANも同様にクラスごとに1枚ずつ画像が保存できるようにしていきます。

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

バイナリ法

バイナリ法

$x=a^k$のとき,2乗計算を$k$回行うことになる.
計算を効率よくする方法として,$a^{2^i}$を順次求めることで,計算量を$log(k)$回に抑える方法がバイナリ法である.

具体例

$5^{21}=5^{2^4}*5^{2^2}*5^{2^0}$

2進数に展開し,左から順に展開することにより計算を実行する.
これにより,$g^k(mod p)$を計算する.

アルゴリズム

binary.py
def Binary(k, g, p):
    k_binary = []
    while(k != 0):
        k_binary.append(k%2)
        k = k//2
        if k == 1:
            k_binary.append(k)
            k = 0
    y = 1
    for i in reversed(range(len(k_binary))):
        if k_binary[i] == 1:
            y = (y*y%p)*g%p
        else:
            y = (y*y%p)
    return y
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

numpyの基本的でないテクニック

numpyのクイックスタートチュートリアルにLess basic(あまり基本的でない)テクニックがあり面白そうと思ったので、理解ついでに紹介。

配列を用いた、配列のインデックス

配列のインデックス(a[i]におけるiの部分)にはスカラー値を持つことが一般的ですが、ここに配列を入れることもできます。

import numpy as np

a = np.arange(12)**2                       
i = np.array([1, 1, 3, 8, 5])             
a[i] #array([ 1,  1,  9, 64, 25], dtype=int32)

何が起こっているのかというと、図のようになります。

npLB1.png

配列iの要素がインデックスとなり、それを用いてaの配列から抽出してくるイメージでしょうか。

インデックスが2次元配列でも適用することができます。その場合、出力も2次元になります。

j = np.array([[3, 6, 7], [5, 9, 7]])      
a[j]                                       
#array([[ 9, 36, 49],
#       [25, 81, 49]], dtype=int32)

チュートリアルの方ではRGBを応用例として出していますが、機械学習で使われるone-hot表現の際にも使えそうですね。

one_hot = np.array([[0, 0, 0], 
                    [1, 0, 0], 
                    [0, 1, 0],      
                    [0, 0, 1]])
number = np.array([[0, 1, 2, 0], 
                  [0, 3, 2, 0]])
one_hot[number]
#array([[[0, 0, 0],
#        [1, 0, 0],
#        [0, 1, 0],
#        [0, 0, 0]],
#
#       [[0, 0, 0],
#        [0, 0, 1],
#        [0, 1, 0],
#        [0, 0, 0]]])

ちなみに、number[one_hot[number]]としても元に戻るわけではないので注意。

また、インデックスには複数の配列を指定することもできます。

a = np.arange(12).reshape(3,4)
#array([[ 0,  1,  2,  3],
#       [ 4,  5,  6,  7],
#       [ 8,  9, 10, 11]])
i = np.array([[0, 1],                     
              [1, 2]])
j = np.array([[2, 1],                     
              [3, 3]])
a[i, j] 
#array([[ 2,  5],
#      [ 7, 11]])

これまたどう処理しているのか解釈が難しいところですが、以下のようになっています。

npLB3.png

配列のインデックスには、リストを指定することもできます。

a = np.arange(3,8)
a
#array([3, 4, 5, 6, 7])
a[[1,3,4]] = 0
a
#array([3, 0, 5, 0, 0])

このときも、リストの各要素がaのインデックスとして処理されます。

リストを使って一気に割り当てる(代入する)こともできますが、リスト内に同じ数値がある場合、割当ては繰り返されて最後の値が代入されます。

a = np.arange(3,8)
a
#array([3, 4, 5, 6, 7])
a[[1,1,4]] = [1,2,3]
a
#array([3, 2, 5, 6, 3])

ブール配列を使用したインデックス付け

配列に論理演算子を与えることで、ブール配列を作ることができます。

ブール配列をインデックスとすることで、Falseとなる要素を取り除いた1次元配列を出力します(配列の形に注意)。

a = np.arange(-3,9).reshape(3,4)
a
#array([[-3, -2, -1,  0],
#       [ 1,  2,  3,  4],
#       [ 5,  6,  7,  8]])
b = a > 0
b                                   
#array([[False, False, False, False],
#       [ True,  True,  True,  True],
#       [ True,  True,  True,  True]])
a[b]                                       
#array([1, 2, 3, 4, 5, 6, 7, 8])

ブール配列をインデックスとする配列に割り当てることで、条件に合った要素へ一度に代入することができます。

a[a<0] = 0                                  
a
#array([[0, 0, 0, 0],
#       [1, 2, 3, 4],
#       [5, 6, 7, 8]])

次元(軸)と同じブール配列を使うことで、より複雑な抽出もできます。

a = np.arange(12).reshape(3,4)
b1 = np.array([False,True,True])             
b2 = np.array([True,False,True,False])     

a[b1,:] #a[b1]でも可                                 
#array([[ 4,  5,  6,  7],
#       [ 8,  9, 10, 11]])

a[:,b2] #a[b2]でも可                             
#array([[ 0,  2],
#       [ 4,  6],
#       [ 8, 10]])

a[b1,b2]                                 
#array([ 4, 10])

図としてみるとこんなかんじ。

npLB4.png

これ、なんでa[b1,b2]が[[4,6],[8,10]]じゃなくて[4,10]なんでしょうね。ドキュメントにもa weird thing to do(奇妙なこと)と書いてあるので、そう覚えるしかないのでしょうか。

まとめ

以上、配列を用いた配列のインデックスと、ブール配列を使用したインデックス付けについて紹介しました。扱いの難しいテクニックですが、使いこなせばきっと役に立つはずです。

冒頭にリンクを貼ったチュートリアルには、他にもテクニックが載っている(うまく理解できなかったため割愛)ので、余裕のある人はぜひ読んでみてください。

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

テンソルネットワークを用いた量子インスパイアな機械学習

はじめに

今回は、近年少しホットになっている量子インスパイア機械学習を紹介します。
ここでの"量子インスパイア"をもう少し正確に言うと、"量子系を古典計算でなるべく効率的にシミュレートするために用いられる手法から着想を得た"です。
具体的にはテンソルネットワークです。

背景

古典系による量子系のシミュレートにテンソルネットワークを用いる流れは以前からありました。多体量子系の状態を行列積状態を用いて効率的に計算する[1]、ゲート量子計算を無向グラフとテンソルネットワークの縮約の組み合わせで効率的にシミュレートする[2]、などです。

これらの手法が示すように、テンソルネットワークを用いることで量子系が持つ非常に高次元の空間をあくまで近似的にですが、古典計算で扱うことができます。
高次元であることは機械学習においては表現力の高さにつながります。

この特徴を量子系のシミュレートだけでなく古典機械学習問題に適用しようというのが今回紹介する"量子インスパイアな機械学習"となります。

行列積状態(復習)

行列積状態はMPSと呼ばれ、下記図のように表されます[1]。

image.png

各黒丸は"site"と呼ばれ、N qubit系であればN個のsiteが作られます。
$\sigma_i$は"physical index"と呼ばれ、qubitの場合は$0\ (|0\rangle)$ or $1\ (|1\rangle)$を指します。各siteは通常の2次元行列にphysical indexの次元を加えた3次元行列(テンソル)と考えます。

各siteはそれぞれphysical index=0に対応する2次元行列と、physical index=1に対応する2次元行列を持つとイメージすることもできます。
仮に全ての$i$について$\sigma_i = 0$の場合、全てのsiteのphysical index=0に対応する2次元行列同士の積を計算することができ、その結果はもとの量子状態における$|00...0>$の係数となります。

どのように機械学習を行うか

教師あり学習による分類問題を扱った代表的な論文[3],[4]をもとに紹介します。
全体的なフローを図に示します。

スクリーンショット 2020-10-20 12.04.03.png

まず、入力データ$x$を行列積状態の図の$\sigma_i\ (i\in 0,...,n-1)$にエンコードします。

エンコードしたデータとMPSとの間でテンソル縮約をとります。
さらにMPSのsite間のエッジを縮約するのですが、このままでは得られるのは1つのスカラー値なので分類に使えません。
そのためMPSにはあらかじめ、$x$が各クラスに属する確率に対応する値を出力するための"label index"を持たせておきます。label indexは既存のsite 1つ、またはlabel indexの保持用に新たに追加したsite 1つに持たせます。
このようにすると、$\sigma_i$とMPSのすべてのエッジの縮約を計算した結果、判別クラス数と要素数の等しいテンソルが残るためその値を損失関数に入力できます。

学習時は損失関数の出力が小さくなるよう、MPSの各要素を更新します。
更新の方針は大きく分けて2通りあります。
1つは[3]で採用されている方法で、DMRGという従来の手法の応用です。隣り合う2 siteのみを変数とした局所的な最適化による更新をsweepしながら繰り返します。
もう1つは[4]で採用されており、誤差逆伝搬法を用いてMPSの全要素を更新します。

前者の手法は更新の際にSVDを用いて余剰次元を動的に刈り込めるメリットがあります。
一方後者の手法は既存のDL, 自動微分フレームワークによる計算との相性が良く、またおそらくネットワーク構造や損失関数の定義などの自由度が高いです。

実装

今回は[4]で行われた、誤差逆伝搬法によるMNIST学習を実装しました。
実装には著者らが開発したTensornetworkというpythonモジュールを使用しました。

Tensornetworkは文字通りテンソルネットワークの計算に適したライブラリです。
バックエンドとして"tensorflow"と"jax"を選択できます。"tensorflow"を選択した場合はTensorflowフレームワークと組み合わせて学習できます。
Tensorflowの自動微分機能や組み込みの関数を使用できる点が便利なのですが、一方で書いてみるとほとんどがカスタムレイヤーで占められてしまうため、フレームワークに合わせて書く面倒さやフレームワーク自体のオーバーヘッドが気になる面もあります。

そこで今回はjaxバックエンドを採用しています。
実際に[4]に続く研究[5]ではjaxバックエンドを使用しているようです。
jaxもpythonフレームワークで、おおざっぱに言うと自動微分、JIT、ベクトル化による並列演算をサポートしたnumpyのようなものです。
Tensorflowの高速な自動微分だけシンプルに使いたい、という場合に良い選択肢なのではないでしょうか(JuliaのFluxなども似たような立ち位置だと思っていて、そういった需要はそれなりにあるのでしょう)。

私の実装は[4]とは以下の点でやや異なっています。
1. MNISTの画像データを2x2 average poolingしている。
2. optimizerは[4]で使用されたadamでなく単純な勾配降下法(それに伴い、学習率やEpoch数も調整)

1.については、オリジナルのサイズだと学習の難易度が高かったためです。縮約をとる際に行われるのは画素数分の行列のかけ算であり、かける行列の数が増えると出力値が発散 or 0収束しやすい、また勾配が消失しやすいなどのプラクティカルな難しさがあります。調整次第ではあると思うのですが今回は妥協しました。
また[5]では(ネットワーク構造やタスクがいくらか異なるためかもしれないですが)著者らもpoolingしています。

2.はサンプル実装をシンプルにするためです。
また手元でtensorflowバックエンドで書いた時にadam optimizerでトライした結果と比較して、勾配降下法が特に劣っていなかったという経緯もあります。

実装コードは以下に置きました。
https://github.com/ryuNagai/MPS/blob/master/TN_ML/MNIST_ML_jax.ipynb
※初回学習実行時、JITコンパイルが3分くらいかかります。どこか改善の余地ありかもしれません。

学習過程はこんな感じです。
image.png

最終的にtrain accuracy=0.962、test accuracy=0.952となりました。
[4]では50 epoch程度でtrain accuracy=0.98程度に到達しており、それを再現するには及ばない結果でした。
裏では[4]の結果が再現できないか条件近くして多少試したのですが、中々難しかったので一旦この値で良しとします。

まとめ

新たに流行る(かもしれない)テンソルネットワークを用いた量子インスパイア機械学習を実装しました。
量子コンピュータのハードウェア面に多くの制約がある現状で、こちらの手法は古典計算機上で実行できるため大きな問題も扱えます。
この手法を用いて従来の機械学習モデルより有用なモデルが多く発見されるかはまだこれからの研究次第だと思います。

加えて、古典機械学習と比較した優位性が量子空間を用いた機械学習にあるかどうか、このような手法を用いることでそれが近似的・間接的にでも見える、検証できる可能性があれば良いと思っています。

参考文献

[1] https://arxiv.org/abs/1008.3477
[2] https://arxiv.org/abs/1805.01450
[3] https://papers.nips.cc/paper/6211-supervised-learning-with-tensor-networks
[4] https://arxiv.org/abs/1906.06329
[5] https://arxiv.org/abs/2006.02516

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

Pythonでメタアナリシス

はじめに

PythonにはPythonMetaというメタ分析を行うパッケージもあるのですが、勉強の意味も兼ねて、今回はpandasでメタアナリシスを実装してみました。

メタアナリシスのモデルには主に Fixed Effect Model (固定効果モデル)Random Effects Model (変量効果モデル) の2種類があるので、それぞれ実装してみます。

サンプルデータ

今回は、以下のデータを使って実験してみます。

import pandas as pd
import numpy as np
data = pd.DataFrame({
    "g": [0.12, 0.23, 0.34, 0.45, 0.42, 0.39, 0.49, 0.65, 0.76, 0.87],
    "V": [0.01, 0.04, 0.03, 0.02, 0.01, 0.02, 0.03, 0.04, 0.02, 0.01]
})

ここで g が Hedges'g (=効果量)、Vが効果量の分散を表しています。

固定効果モデル

固定効果モデルではシンプルに効果量の分散の逆数をその項目の重みとします。したがって求める平均効果量はそれぞれの項目の効果量に重みをかけたものを全体の重みで割ったものとして計算されます。計算式は以下の通りです。Pythonでも3行で書けtしまいます。

# 固定効果モデル
data['W'] = 1 / data.V
data['Wg'] = data['g'] * data['W']
result = data['Wg'].sum() / data['W'].sum()
result
>> 0.4776

変量効果モデル

変量効果モデルでは重みを計算する部分が少し複雑になります。研究間の効果量のばらつきを考慮するために、一旦効果量の平均をとったうえで、それに対する各項目の偏差を重みの計算に組み込みます。計算式は以下の通りです。

g_hat = data.g.mean()
Q = (data.W * (data.g - g_hat)**2).sum()
data['W2'] = data.W ** 2
C = data.W.sum() - (data.W2.sum()/data.W.sum())
d = len(data) - 1
# 研究間分散
if (Q-d) > 0:
    V_between = (Q - d) / C
else:
    V_between = 0
data['V_str'] = data.V + V_between
data['W_str'] = 1 / data.V_str
result = (data.g * data.W_str).sum() / data.W_str.sum()
result

その他

固定効果モデルでも変量効果モデルでも、計算された重みの和から95%信頼区間を計算することができます。

std_err = np.sqrt(1/data.W_str.sum())
lwr = result - 1.96 * std_err
upr = result + 1.96 * std_err
[lwr, upr]

参考文献

『メタ分析入門 心理・教育研究の系統的レビューのために』山田剛史・井上俊哉編

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

Machine Learning : Supervised - Random Forest

目標

ランダムフォレストを理解して、scikit-learn で試す。

理論

ランダムフォレストは、複数の決定木を組み合わせるバギングと呼ばれるアンサンブル学習の 1つです。

ノーフリーランチ定理

ノーフリーランチ定理とは、元々は組み合わせ最適化において、考え得るすべての問題に探索アルゴリズムを適用した場合、その平均性能はどのアルゴリズムも同じようなものになるというものです。

これは、各アルゴリズムにはそれぞれ満たすべき前提条件があり、考え得るすべての問題がその前提条件を満たすことはないため、ある問題に対しては良い成果を発揮しても、別の問題では他のアルゴリズムよりも悪い性能になってしまうことから、すべての問題で他のアルゴリズムよりも良いアルゴリズムはないことを示しています。

そこから発展して、機械学習においてはどんな問題でも最良の結果が得られる万能な学習器が存在しないことを主張する際に引用されます。

バギング

前述のノーフリーランチ定理により、どんな問題にも最適な万能な学習器は存在しないことが分かっています。そこで、複数の学習器を組み合わせるという方法を思いつくのは自然な発想です。

複数の学習器からの出力の多数決を取って最終的な出力とするといった学習法はアンサンブル学習と呼ばれています。アンサンブル学習に使用される個々の識別器は、ランダムより少し良い程度の性能であればよいので、弱識別器と呼ばれています。

アンサンブル学習の代表的な方法として、バギング (Boostrap AGGregatING) があります。バギングは、下図のように学習データのブートストラップサンプルを用いて複数の識別器を学習させ、新しいデータに対しては分類では多数決によりカテゴリーを、回帰ではその平均により推定値を出力します。

108_bagging.png

バギングは個々の識別器を独立に並列で学習することができますが、ブートストラップサンプリングでは重複を許しているため弱識別器として決定木を用いると決定木同士の相関が高くなり、どれも似通ったものになってしまう可能性があります。

このような問題を改善したのがランダムフォレストです。

ランダムフォレスト

ランダムフォレストの学習では、ブートストラップサンプルで決定木を学習する際、すべての特徴量を用いるのではなく、指定しておいた数だけ特徴量をランダムに選択し、その特徴量を用いて決定木を構築します。

バギングではブートストラップサンプルから決定木を構築していましたが、ランダムフォレストでは下図のようにブートストラップサンプルにおいて使用する特徴量をランダムに選択して決定木を構築します。

108_random_forest.png

このように各ブートストラップサンプルにおいて使用する特徴量をランダムにすることによって各決定木が多様性をもつようになり、バギングで問題となっていた決定木間の相関を減らすことが期待できます。

scikit-learn では引数 n_estimators で弱識別器の数を、引数 max_features で使用する特徴量の数を指定することができます。使用する特徴量の数はデフォルトでは特徴量の平方根数を使用するようになっています。

実装

実行環境

ハードウェア

・CPU Intel(R) Core(TM) i7-6700K 4.00GHz

ソフトウェア

・Windows 10 Pro 1909
・Python 3.6.6
・matplotlib 3.3.1
・numpy 1.19.2
・scikit-learn 0.23.2

実行するプログラム

実装したプログラムは GitHub で公開しています。

random_forest.py

結果

ランダムフォレストによる分類

これまでに使用してきた breast cancer dataset にランダムフォレストを適用した結果を示します。

Accuracy 92.98%
Precision, Positive predictive value(PPV) 94.03%
Recall, Sensitivity, True positive rate(TPR) 94.03%
Specificity, True negative rate(TNR) 91.49%
Negative predictive value(NPV) 91.49%
F-Score 94.03%

下図は、iris dataset に対して多クラス分類を実行した際の識別境界を表しています。

108_random_forest_classification.png

ランダムフォレストによる回帰

回帰問題のデータは正弦波に乱数を加えました。回帰では平均値を最終的な出力値にします。

108_random_forest_regression.png

参考

1.11.2. Forests of randomized trees

  1. Leo Breiman, "Random forests", Machine learning 45.1 (2001): pp. 5-32.
  2. 平井有三.『はじめてのパターン認識』,森北出版,2012.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Julia早引きノート[08]変数の型(Int, Float, Bool, Char, String)

変数の型(Int, Float, Bool, Char, String)(書き方例)

note08
◆数字の型
 Int
  Int8
  Int16
  Int32
  Int64
  Int128
 UInt
  UInt8
  UInt16
  UInt32
  UInt64
  UInt128
 Float
  Float16
  Float32
  Float64

◆真偽の型
 Bool

◆文字列の型
 Char
 String

◆型の確認
 typeof()

解説

Int型
Int型は符号付き整数型で、Int8,Int16,Int32,Int64,Int128があります。
Int8は8ビット、Int16は16ビット、同様に32ビット、64ビット、128ビットです。
UInt
UInt型は符号無し整数型です。
Bool
Bool型は true または false です。
Float
Float型は浮動小数点です。
文字列型
Charは一文字の型を表します。
Stringは一連の文字列の型を表します。

Char型の確認↓
image.png
String型の確認↓
image.png

もくじ

Julia早引きノート[01]変数・定数の使い方
Julia早引きノート[02]算術式、演算子
Julia早引きノート[03]複素数
Julia早引きノート[04]正規表現
Julia早引きノート[05]if文
Julia早引きノート[06]ループ処理
Julia早引きノート[07]try, catch, finally
Julia早引きノート[08]変数の型(Int, Float, Bool, Char, String)

(※引き続きコンテンツを増やしていきます)

関連情報

:paperclip: Julia - 公式ページ
https://julialang.org/

:paperclip: Julia - 日本語公式ドキュメント
https://julia-doc-ja.readthedocs.io/ja/latest/index.html

:paperclip: 初めてのJuliaとインストール (Windows & Linux)
https://qiita.com/ttlabo/items/b05bb43d06239f968035

:paperclip: Julia - Mathematics
https://docs.julialang.org/en/v1/base/math/

ご意見など

ご意見、間違い訂正などございましたらお寄せ下さい。

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

【Python】iterdir()など使用時に出る[Errno 20] Not a directory: '***/.DS_Store'

Pythonでiterdir()などを使うと、タイトルのようなエラーが出てきます。
Mac独自の.DS_Storeという隠しファイルのせいで、関数が実行できない時出てきます。
対処としては、.DS_Storeを消せば良いだけです。

そもそも.DS_Storeとは

.DS_Store は、Finderがフォルダの設定を記録するためのファイルです。
参考サイト

対処

ファインダーでもコマンドラインでもいいので消します。

コマンドラインの場合、
邪魔な.DS_Storeが入っているフォルダに移動し

find . -name ".DS_Store" -delete

で消します。
参考サイト

終了です。

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

【Python】 指定した期間の日時リスト作成

やりたいこと

2020年10月29日 05:00から2020年11月1日 00:00の期間で6時間間隔のリストを作成したい

pythonコード

from datetime import datetime, timedelta

def perdate(start, end, delta):
    current = start
    while current < end:
     yield current
     current += delta

for result in perdate(datetime(2020, 10, 29, 5,0,0), datetime(2020, 11, 1, 0,0,0), timedelta(hours=6)):
    print (result)

実行結果

2020-10-29 05:00:00
2020-10-29 11:00:00
2020-10-29 17:00:00
2020-10-29 23:00:00
2020-10-30 05:00:00
2020-10-30 11:00:00
2020-10-30 17:00:00
2020-10-30 23:00:00
2020-10-31 05:00:00
2020-10-31 11:00:00
2020-10-31 17:00:00
2020-10-31 23:00:00

参考記事

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

JuliaでNumPyのadd.atを再現してみる

タイトルそのまま。

再現したいNumPyのコード

NumPyではin-placeな変更である。

Python(NumPy)
>>> A = np.ones((3,3))
>>> A
array([[1., 1., 1.],
       [1., 1., 1.],
       [1., 1., 1.]])
>>> B = np.array([[1, 1, 1], [2, 2, 2]])
>>> B
array([[1, 1, 1],
       [2, 2, 2]])
>>> np.add.at(A, [0, 2], B)
>>> A
array([[2., 2., 2.],
       [1., 1., 1.],
       [3., 3., 3.]])

Juliaのコード

+=演算子に.演算子を付加して.+=とし,ブロードキャストを行っていることに注意。
+=はブロードキャストを行わず,in-placeな変更ではない。
しかし,.+=では(当然)ブロードキャストを行うが,in-placeな変更である1

Julia
julia> A = ones(3,3)
3×3 Array{Float64,2}:
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0

julia> B = [1. 1. 1.; 2. 2. 2.]
2×3 Array{Float64,2}:
 1.0  1.0  1.0
 2.0  2.0  2.0

julia> selectdim(A, 1, [1, 3]) .+= B
2×3 view(::Array{Float64,2}, [1, 3], :) with eltype Float64:
 2.0  2.0  2.0
 3.0  3.0  3.0

julia> A
3×3 Array{Float64,2}:
 2.0  2.0  2.0
 1.0  1.0  1.0
 3.0  3.0  3.0

Sources

numpy.ufunc.at — NumPy v1.19 Manual
Arrays · The Julia Language
Multi-dimensional Arrays · The Julia Language
Mathematical Operations and Elementary Functions · The Julia Language

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

人生曲線を python で描いてみる

タイトル通りです.

人生曲線とは,これまでの自分の経験を時系列で振り返り,
x軸を年齢,y軸をモチベーション(幸福度,充実度)として,プロットしたものです.

次に,y軸が高くなっていくところ,低くなっていくところ,に着目し,
なぜそのようになったのかを繰り返すことで,自分の本質的な価値観が分かる,という仕掛けです.

記載のコードの使い方は,自分の経験に合わせて,x, y のデータを改変し,
「ああ,そうそうこんな感じ」というようなグラフが出力される次数を探します.

lifeCurve
import numpy as np
import matplotlib.pyplot as plt

x = np.array([15.0, 16.5, 18.0, 21, 24.0, 25, 26, 27, 28, 29, 30, 33, 35, 37, 45, 53, 62])
y = np.array([15.0, 10.0, 20.0, 30, 24.0, 20, 15, 12, 10, 18, 25, 40, 50, 100, 70, 80, 120])
xp = np.linspace(min(x), max(x), 1000)

plt.rcParams["font.size"] = 20
fx = np.poly1d(np.polyfit(x, y, 6)) # ← 次数はいま6.ここを調整.
fig, ax = plt.subplots(figsize=(20,10))
ax.plot(xp, fx(xp), '-', color='blue')
ax.scatter(x, y, color='deepskyblue', s=32)
ax.axhline([0], color='black')
ax.set_xlim(min(x), None)
ax.set_ylim(0, 1.2*max(y))
ax.set_ylabel('Motivation Level')
ax.set_xlabel('My age')

color = '#1e90ff'
p1 = np.linspace(20, 23)
ax.fill_between(p1, fx(p1), 0, facecolor=color, alpha=0.5)
p2 = np.linspace(28, 40)
ax.fill_between(p2, fx(p2), 0, facecolor=color, alpha=0.5)
p3 = np.linspace(50, 60)
ax.fill_between(p3, fx(p3), 0, facecolor=color, alpha=0.5)
fig.savefig('./data/img/lifeCurve.png'))

lifeCurve.png

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

PHPでファイルをダウンロードする【作成中】

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

内挿と外挿の範囲をpythonで可視化

内挿と外挿の範囲を理解するために作ったコード.

過学習への注意も必要だが,外挿の範囲において,
得られた近似式が適用可能かどうかは十分に吟味しないといけない.

また,近似する目的が「予測」ではなく,「分析」である場合,
必要以上に変数を多くしたり,次数を上げると,解釈が難しくなるので,
如何に簡単,というかシンプルに仕立てられるか,が課題(腕の見せ所?).

赤破線部:外挿範囲,青実線部:内挿範囲 としています.

interpolation_and_extrapolation
import numpy as np
import matplotlib.pyplot as plt

x = np.array([2.0, 3.5, 4.0, 4.5,  5.0,  5.5])
y = np.array([3.0, 3.2, 3.9, 5.2, 8.4, 10.5])
xp = np.linspace(2, 5.5, 100)
xp1 = np.linspace(0, 2, 100)
xp2 = np.linspace(5.5, 8, 100)

for val in range(1, 6):
    fx = np.poly1d(np.polyfit(x, y, val))
    plt.rcParams["font.size"] = 20
    fig, ax = plt.subplots(figsize=(15, 10))
    ax.plot(xp, fx(xp), '-', color='blue')
    ax.plot(xp1, fx(xp1), '-', color='red', linestyle='dashed')
    ax.plot(xp2, fx(xp2), '-', color='red', linestyle='dashed')
    ax.scatter(x, y, color='deepskyblue', s=32)
    s = '$y =$'
    for idx, deg in enumerate(reversed(range(0, val+1))):
        if (fx.coef[idx] > 0) & (idx != 0):
            s += '$ +$'
        if deg > 1:
            s += f' ${fx.coef[idx]:.2f} x^{deg}$'
        if deg == 1:
            s += f' ${fx.coef[idx]:.2f} x$'
        if deg == 0:
            s += f' ${fx.coef[idx]:.2f}$'
#     ax.text(0.05, 0.8, s=s, size='x-large', transform=ax.transAxes)
    ax.axhline([0], color='black')
    ax.axvline([2], color='gray', linestyle='dotted')
    ax.axvline([5.5], color='gray', linestyle='dotted')
    ax.set_xlim(0, 8)
    ax.set_ylim(-3, 14)
    ax.set_title(s)
    ax.set_ylabel('response variable')
    ax.set_xlabel('explanatory variables')
    fig.savefig(f'./data/img/inter_and_extrapolation{val}.png')

1次近似
inter_and_extrapolation1.png
2次近似
inter_and_extrapolation2.png
3次近似
inter_and_extrapolation3.png
4次近似
inter_and_extrapolation4.png
5次近似
inter_and_extrapolation5.png

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

__name__

test.py
print(__name__)
出力
__main__
test2.py
import test
出力
test

test.pyを直接実行するとnameにstr型のmainが代入される。
またtest.pyを外部からインポートするとstr型のファイル名testが代入される。つまり、

if __name__ == '__main__':
    main()

のmain()は外部からインポートされた場合は実行されず、直接実行した場合のみ実行される。

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

【Python】Gitubのipynbファイルを日本語に翻訳してダウンロードするnotebook

はじめに

前回、プログラムのコメントを日本語に翻訳するスクリプトを書きましたが、同じ要領でipynbファイル(Jupyterのファイル)ごと翻訳できるだろうということで書いてみました。
Githubに上げといたのでColaboratoryから実行できるようにリンクを張っておきます。→ ipynbTranslator.ipynb - Colaboratory

使い方

Githubに上がっているipynbファイルのURLをペーストして実行すると、翻訳されたものがダウンロードされます。
image.png

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

【Python】GitHubのipynbファイルを日本語に翻訳してダウンロードするnotebook

はじめに

前回、プログラムのコメントを日本語に翻訳するスクリプトを書きましたが、同じ要領でipynbファイル(Jupyterのファイル)ごと翻訳できるだろうということで書いてみました。
GitHubに上げといたのでColaboratoryから実行できるようにリンクを張っておきます。→ ipynb_translator.ipynb - Colaboratory

使い方

GitHubに上がっているipynbファイルのURLをペーストして実行すると、翻訳されたものがダウンロードされます。
image.png

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

goodnote5のフラッシュカードをAnkiに取り込む

自己紹介

趣味でプログラムを書いている学生。for文がわかる。

環境

macOS Catalina 10.15.7
python 3.8.6

動機

goodnotes5でフラッシュカードを作ってAnkiで学習したい。
goodnotes5にあるフラッシュカードの試験的な機能は今のところイマイチ。

流れ

3,4番をpythonでやる。
1. goodnotes5からフラッシュカード(下の画像)をイメージ形式で書き出す文法特急暗記用-78.jpg
2. 書き出したイメージ(いっぱい)を任意のディレクトリに格納しておく
3. 書き出したイメージを2分割してそれぞれをAnkiのcollection.mediaに保存
4. 2分割したイメージをAnkiで表示できるようなcsvファイルを作成する(example.csv)
5. PC版Ankiでcsvをimportする

example.csv
<img src="example_qst0.png"><img src="example_ans0.png">
<img src="example_qst1.png"><img src="example_ans1.png">
<img src="example_qst2.png"><img src="example_ans2.png">

実際のコード

命名規則、英文法わかりません。
path/to/以下は環境に合わせて変更する。

toAnki.py
import os
from PIL import Image

# Anki用csv作成のため宣言
Anki_csv = []

# Ankiのメディアファイルの場所
anki_media_path = '/path/to/collection.media'

# 画像ファイルの入ったフォルダを指定
print('tell me target dir under homedir')
target_dir_path = '/path/to/userhome'+input()

# 画像ファイルの名前を指定
print('tell me image file name')
image_file_name = input()

# 画像ファイルの名前を指定
print('tell me image index start')
index = int(input())

# 指定したフォルダ内の画像ファイルを全て取得
target_list = os.listdir(target_dir_path)
target_list.remove('.DS_Store')


# 各画像ファイルごとに画像処理・保存
for target in target_list:

    # 画像ファイルをImageオブジェクトに格納
    img = Image.open(target_dir_path + '/' + target)

    # 画像の幅と高さを取得
    width = img.size[0]
    height = img.size[1]

    # 画像を上と下に分割・保存
    img_qst = img.crop((0, 0, width, height/2))
    img_ans = img.crop((0, height/2, width, height))
    img_qst.save(anki_media_path + '/' + image_file_name + '_qst' + str(index)+ '.png')
    img_ans.save(anki_media_path + '/' + image_file_name + '_ans' + str(index)+ '.png')

    # csv用
    qst_tag = '<img src="' + image_file_name + '_qst' + str(index)+ '.png' + '">'
    ans_tag = '<img src="' + image_file_name + '_ans' + str(index)+ '.png' + '">'
    Anki_csv.append(qst_tag + ',' + ans_tag + '\n')

    # 終わったら逐一報告
    print('done ' + str(index))

    # 保存する画像ファイルのインデックスをカウント
    index += 1

# csv作成
with open('/path/to/Desktop/toAnki.csv',mode='w') as f:
    for j in Anki_csv:
        f.write(j)

print('All work was done')

さいごに

markdown難しい。コードの中身について言及していない。
質問・ご指摘等よろしく。

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

goodnotes5とAnkiの連携を助けるスクリプトをを書いた

自己紹介

趣味でプログラムを書いている学生。for文がわかる。

環境

macOS Catalina 10.15.7
python 3.8.6

動機

goodnotes5でフラッシュカードを作ってAnkiで学習したい。
goodnotes5にあるフラッシュカードの試験的な機能は今のところイマイチ。

流れ

3,4番をpythonでやる。
1. goodnotes5からフラッシュカード(下の画像)をイメージ形式で書き出す文法特急暗記用-78.jpg
2. 書き出したイメージ(いっぱい)を任意のディレクトリに格納しておく
3. 書き出したイメージを2分割してそれぞれをAnkiのcollection.mediaに保存
4. 2分割したイメージをAnkiで表示できるようなcsvファイルを作成する(example.csv)
5. PC版Ankiでcsvをimportする

example.csv
<img src="example_qst0.png"><img src="example_ans0.png">
<img src="example_qst1.png"><img src="example_ans1.png">
<img src="example_qst2.png"><img src="example_ans2.png">

実際のコード

命名規則、英文法わかりません。
path/to/以下は環境に合わせて変更する。

toAnki.py
import os
from PIL import Image

# Anki用csv作成のため宣言
Anki_csv = []

# Ankiのメディアファイルの場所
anki_media_path = '/path/to/collection.media'

# 画像ファイルの入ったフォルダを指定
print('tell me target dir under homedir')
target_dir_path = '/path/to/userhome'+input()

# 画像ファイルの名前を指定
print('tell me image file name')
image_file_name = input()

# 画像ファイルの名前を指定
print('tell me image index start')
index = int(input())

# 指定したフォルダ内の画像ファイルを全て取得
target_list = os.listdir(target_dir_path)
target_list.remove('.DS_Store')


# 各画像ファイルごとに画像処理・保存
for target in target_list:

    # 画像ファイルをImageオブジェクトに格納
    img = Image.open(target_dir_path + '/' + target)

    # 画像の幅と高さを取得
    width = img.size[0]
    height = img.size[1]

    # 画像を上と下に分割・保存
    img_qst = img.crop((0, 0, width, height/2))
    img_ans = img.crop((0, height/2, width, height))
    img_qst.save(anki_media_path + '/' + image_file_name + '_qst' + str(index)+ '.png')
    img_ans.save(anki_media_path + '/' + image_file_name + '_ans' + str(index)+ '.png')

    # csv用
    qst_tag = '<img src="' + image_file_name + '_qst' + str(index)+ '.png' + '">'
    ans_tag = '<img src="' + image_file_name + '_ans' + str(index)+ '.png' + '">'
    Anki_csv.append(qst_tag + ',' + ans_tag + '\n')

    # 終わったら逐一報告
    print('done ' + str(index))

    # 保存する画像ファイルのインデックスをカウント
    index += 1

# csv作成
with open('/path/to/Desktop/toAnki.csv',mode='w') as f:
    for j in Anki_csv:
        f.write(j)

print('All work was done')

さいごに

markdown難しい。コードの中身について言及していない。
質問・ご指摘等よろしく。

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

【Python・Jupyter】クリップボードにコピーしたプログラムのコメントを翻訳して新しいセルに挿入する

はじめに

日本語訳のないライブラリのドキュメントを読むときなど、地味に解釈に苦労することなどありますよね。
英語をスラスラ読めない方だとブラウザの機能で翻訳することが多いと思いますが、コメントだけでいいのにプログラム部分まで翻訳されてしまうのは困りものです。

そこで、Jupyter Notebook用にコメント部分だけ翻訳して、
どうせならシンタックスハイライト機能も活かしたいということで、新しいセルに貼り付けるごくシンプルなコードを書いてみました。

コード

from googletrans import Translator
import re
import pyautogui
import pyperclip as ppc
translator = Translator()

pyautogui.hotkey('b', 'enter')
code = ppc.paste()
en = re.findall("#\s*(.+?)\n", code)
ja = [translator.translate(e, dest="ja").text for e in en]
for e, j in zip(en, ja):
    code = code.replace(e, j)
ppc.copy(code)
pyautogui.hotkey('ctrl', 'v')

使用した様子

元コード

What is PyRPL?
# import pyrpl library
import pyrpl

# create an interface to the Red Pitaya
r = pyrpl.Pyrpl().redpitaya

r.hk.led = 0b10101010  # change led pattern

# measure a few signal values
print("Voltage at analog input1: %.3f" % r.sampler.in1)
print("Voltage at analog output2: %.3f" % r.sampler.out2)
print("Voltage at the digital filter's output: %.3f" % r.sampler.iir)

# output a function U(t) = 0.5 V * sin(2 pi * 10 MHz * t) to output2
r.asg0.setup(waveform='sin',
             amplitude=0.5,
             frequency=10e6,
             output_direct='out2')

# demodulate the output signal from the arbitrary signal generator
r.iq0.setup(input='asg0',   # demodulate the signal from asg0
            frequency=10e6,  # demodulaltion at 10 MHz
            bandwidth=1e5)  # demodulation bandwidth of 100 kHz

# set up a PID controller on the demodulated signal and add result to out2
r.pid0.setup(input='iq0',
             output_direct='out2',  # add pid signal to output 2
             setpoint=0.05, # pid setpoint of 50 mV
             p=0.1,  # proportional gain factor of 0.1
             i=100,  # integrator unity-gain-frequency of 100 Hz
             input_filter = [3e3, 10e3])  # add 2 low-passes (3 and 10 kHz)

# modify some parameters in real-time
r.iq0.frequency += 2.3  # add 2.3 Hz to demodulation frequency
r.pid0.i *= 2  # double the integrator unity-gain-frequency

# take oscilloscope traces of the demodulated and pid signal
data = r.scope.curve(input1='iq0', input2='pid0',
                     duration=1.0, trigger_source='immediately')


In[2]のセルを実行するとその下のセルが自動で生成されます。
image.png

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

AmazonのデータをKeepAPIを使って取得しよう #1データの取得

アマゾンデータを取得する

仕事でアマゾン(US)のデータを取得する必要が出たため備忘録のため。
商品概要、レビューに関してはスクレイピングでも取得はできると思うのですが
過去の出品状況や価格・ランキングなどは取得できないので
KeepaAPIを使って取得していきます。

参考:Keepa
https://keepa.com/#!api

Pythonによるデータ取得加工

keepa_analysis.py
#ライブラリー
!pip install keepa
import keepa
import pandas as pd

#データ取得
accesskey = '##############' # APIkeyを取得し入力してください
api = keepa.Keepa(accesskey)
products = api.query('B07VQJ5P3L') # ここにはASINを入れるとデータを勝手に取ってくれます
keepa.plot_product(products[0])

ダウンロード.png
ダウンロード (1).png
実行してみるとこんなデータが取れます。

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

Chainerによる機械学習のためのPython学習メモ 10章 Cupy 入門

What

Chainerを利用して機械学習を学ぶにあたり、私自身が、気がついた点、リサーチした内容をまとめる記事になります。今回は、scikit-learnを勉強します。

私の理解に基づいて記述しているため、間違っている場合があります。間違いは都度修正するつもりです、ご容赦ください。

Content

GPU

画像処理に特化した演算装置。CPUと原理上何が異なっていて画像処理に有利なのか全く理解できていない。
例えば、下記サイトではCPUとGPUの違いが簡単に説明されている
https://www.datadock.co.jp/column/GPU/2018/05/88.html
ただ本質的な理解をするためには、まずCPUが得意とする処理、GPUが得意とする処理を抑える必要がありそうだ。
定正的な話だけをするなら、似たような処理を同時に複数行うのは現在、並列演算が高速化を可能とする領域であること。数値計算を並列かつ高速に処理できるのがGPUということだけはわかった。
ちなみに、GPUのメーカーNDIVIAは最近ひそやかに注目を浴びていてAIの流れが大きいみたいですね。
CupyではGPUを必要としますが、なんとColabではGPUも使用可能とのこと、すごい!

Cupy

内容読んでも、正直そんなにまとめることがないです。。。
Cupyで記述することでGPUで数値計算することができ、高速動作が期待できる。
できるだけ数値計算はNumpyではなくCupyを使って記述することで高速動作するコードがかける。書き方に困ったら適宜調べるという形で大丈夫そう

Comment

気温がガクッと下がり体調を崩しやすい時期ですね、、、
土日は頭痛と眠気が酷く、寝こんでしまいましたorz

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