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

ゼロから始めるDjango(part:1)

理由

仕事をしていてDjangoのライブラリに関連するものが多く、一度触ってみたいと思ったため

目的

  • アプリケーションの作成の流れを理解する
  • 使うことができるライブラリを理解する

参考文献

はじめての Django アプリ作成、その 1 | Django ドキュメント | Django

実践

djangoのプロジェクトのスタート

django-admin startproject mysite

上記コードを実行するとdjango用のプロジェクトをmysiteという名前で作成してくれる。

プロジェクトの中身

そのプロジェクト構造は以下のようになっている。

mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py

*外側のmysiteは重要ではないため好きな名前でよい

  • manage.py プロジェクトの様々な操作を行うやつ
  • 内側のmysiteはPythonパッケージ
  • mysite/settings.py: Django プロジェクトの設定ファイル
  • mysite/urls.py: Django プロジェクトの URL 宣言(コントローラーみたいなやつ?)
  • mysite/asgi.py: プロジェクトを提供する ASGI 互換 Web サーバーのエントリポイント
    エントリポイント:プログラムを実行するうえで、プログラムやサブルーチンの実行を開始する場所のこと(main.pyみたいなやつ)

  • mysite/wsgi.py: Webサーバー

開発用サーバー

manage.pyのあるディレクトリ、つまりは外側のmysiteで上記コードを実行すると立ち上がる。

python manage.py runserver

サーバーのポートを変えたい場合は下記のようにする。

python manage.py runserver 8080

pollsアプリケーション

作成準備

manage.pyと同じ階層で下記のコマンドを実行

python manage.py startapp polls

pollsというディレクトリが自動生成される。中身は下記のようになっている。

polls/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py

pollsの部分は任意。startappが大事。

urls.pyを追加し、urls.pyとviews.pyを作成する。

polls/views.py
from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")
polls/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

ルートURLconf

mysite/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]
  • urlpatternsのリスト内にurlconfを追加。
  • djangoではinclude()に遭遇するとそのポイントまでに一致したurlのところまでを切り取る。
  • 次の処理のために残りの文字列をインクルードされたURLconfへ渡す。 http://polls/hogehoge/ の場合は http://polls/(ここまで切り落とす), hogehoge/(ここまでをpolls.urlsへ渡す)

実行結果の確認

下記のコマンドで実行結果を確認する

$ python manage.py runserver

http://localhost:8000/polls/ にアクセスして、
「Hello, world.You`re at the polls index.」と表示されていれば成功

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

PytorchのDatasetを層状にfoldingする

既存のDatasetをN-foldするときに、先頭から順に1,2,...N,1,2,...,N,1,2,...,N,1,2,...とデータセットを分割する。時系列データを分割するときに、特定の月や季節にデータが偏らないようにするときに使用する。

from torch.utils.data import Dataset


class LayeredFoldWrapper(Dataset):
    def __init__(self, dataset, n_splits=5, fold=0, valid=False):
        self.dataset = dataset
        self.n_splits = n_splits
        self.fold = fold
        self.valid = valid
        self.valid_index = list(self._valid_index(len(dataset), n_splits, fold))
        self.train_index = list(set(range(len(dataset))) - set(self.valid_index))

    def __len__(self):
        return len(self._get_index_list(self.valid))

    def __getitem__(self, i):
        return self.dataset.__getitem__(self._get_index_list(self.valid)[i])

    def _valid_index(self, N, n_splits, fold):
        """
        N: 全データの数
        n_splits: foldのスプリットの数
        fold: 各foldを指定する値 0<=fold<=n_splits-1
        """
        assert(0<=fold<=n_splits-1)
        return range(n_splits - fold - 1, N+1, n_splits)

    def _get_index_list(self, valid):
        if valid:
            return self.valid_index
        else:
            return self.train_index
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

超個人的略語備忘録

Linuxコマンド

  • apt : Advanced Packaging Tools

  • sudo : SUbstitute user DO / SUperuser DO

  • pwd : Print Working Directory

  • cd : Change Directory

  • ls : List Segment

  • cp : CoPy

  • mv : MoVe

  • rm : ReMove

  • cat : CATenate (連結する)

Python

  • pip : Pip Install Package/Python

その他

  • GUI : Graphical User Interface

  • CUI : Character User Interface / Character-based User Interface / Console User Interface

  • CLI : Command Line Interface

  • CPU : Central Processing Unit

  • GPU : Graphical Processing Unit

  • IDE : Integrated Development Environment

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

Awesome Deep Learning

aa

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

Codeforces Round #486 (Div. 3) バチャ復習(9/23)

今回の成績

スクリーンショット 2020-09-23 16.46.13.png

今回の感想

D問題を永遠にバグらせていたら一時間半を無駄にしました。E問題も面倒だと思って避けましたが、結果的に場合分けをきっちりしていけばupsolveできたので悔しい限りです。D,E問題共にコーナーケースやバグを発見することができなかったのも今までの反省が生かせていないので次に生かせるように頭の中を整理しておこうと思います。

A問題

レーティングが異なるようにしたいので、set構造でレーティングの種類数を数えて$k$を超えるか判定します。$k$以上の場合は適当な$k$個を出力します。ここでは実装が楽になるようにindexを使いましたが、計算量的には別の方法が良いです(ここでは紹介しませんが$O(k)$でできます)。

A.py
n,k=map(int,input().split())
a=list(map(int,input().split()))
b=list(set(a))
if len(b)>=k:
    print("YES")
    ans=[]
    for i in range(k):
        ans.append(a.index(b[i])+1)
    print(" ".join(map(str,ans)))
else:
    print("NO")

B問題

題意を満たす文字列の順番がある時、(最初の文字列を除く)任意の文字列は直前の文字列を部分列として持つので、この文字列の順番は長さの昇順になります($\because$背理法により示せます。)。

よって、与えられた文字列を長さの昇順に直し、それぞれ直前の文字列に含むかをinによって判定すれば良いです。この時、計算量は$O(N^2)$となります。

B.py
n=int(input())
a=[input() for i in range(n)]
a.sort(key=lambda x:len(x))
for i in range(n-1):
    if a[i] not in a[i+1]:
        print("NO")
        break
else:
    print("YES")
    for i in range(n):
        print(a[i])

C問題

二つのsequenceを選んでそれぞれから一つずつ要素を抜き出して和が同じものを見つけます。ここで、$i$番目の長さ$n_i$のsequenceから一つ要素を抜き出す和はどの要素を抜き出すかで$n_i$通りあります。また、制約より$\sum_{i=1}^{k}{n_i} \leqq 2 \times 10^5$なので、この和は全列挙することができます

ここで、$i$番目の長さ$n_i$のsequenceの$j$番目の要素を抜き出した時の和は($i$番目のsequenceの和)-($j$番目の要素の値)となり、それぞれのsequenceの和を予め求めておけば、$O(\sum_{i=1}^{k}{n_i})$で求めることができます。また、keyをsequenceの和の値,valueを"その値になる(sequenceのインデックス,抜き出した要素のインデックス)"を保存したvectorとして辞書$m$を置けば、先ほど求めたsequenceの和を順に格納することができます。また、この際にkeyが等しいものの中でsequenceのインデックスが異なるものが二つ以上あれば答えとして出力することができます。逆に二つ以上あるものが一つもない場合はNoを出力すれば良いです。

(気づかなかったですが、Pythonでも書けましたね。なぜC++で書いたのでしょうか…?)

C.cc
//デバッグ用オプション:-fsanitize=undefined,address

//コンパイラ最適化
#pragma GCC optimize("Ofast")

//インクルードなど
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

//マクロ
//forループ
//引数は、(ループ内変数,動く範囲)か(ループ内変数,始めの数,終わりの数)、のどちらか
//Dがついてないものはループ変数は1ずつインクリメントされ、Dがついてるものはループ変数は1ずつデクリメントされる
//FORAは範囲for文(使いにくかったら消す)
#define REP(i,n) for(ll i=0;i<ll(n);i++)
#define REPD(i,n) for(ll i=n-1;i>=0;i--)
#define FOR(i,a,b) for(ll i=a;i<=ll(b);i++)
#define FORD(i,a,b) for(ll i=a;i>=ll(b);i--)
#define FORA(i,I) for(const auto& i:I)
//xにはvectorなどのコンテナ
#define ALL(x) x.begin(),x.end() 
#define SIZE(x) ll(x.size()) 
//定数
#define INF 1000000000000 //10^12:∞
#define MOD 1000000007 //10^9+7:合同式の法
#define MAXR 100000 //10^5:配列の最大のrange
//略記
#define PB push_back //挿入
#define MP make_pair //pairのコンストラクタ
#define F first //pairの一つ目の要素
#define S second //pairの二つ目の要素

signed main(){
    //入力の高速化用のコード
    //ios::sync_with_stdio(false);
    //cin.tie(nullptr);
    ll k;cin>>k;
    map<ll,vector<pair<ll,ll>>> m;
    REP(i,k){
        ll ni;cin>>ni;
        vector<ll> a(ni);REP(j,ni)cin>>a[j];
        ll s=0;REP(j,ni)s+=a[j];
        REP(j,ni){
            m[s-a[j]].PB(MP(i+1,j+1));
        }
    }
    FORA(i,m){
        if(SIZE(i.S)>1){
            map<ll,ll> x;
            //同じ数列被り排除
            FORA(j,i.S){
                x[j.F]=j.S;
            }
            if(SIZE(x)>1){
                auto y=x.begin();
                cout<<"YES"<<endl;
                cout<<y->F<<" "<<y->S<<endl;
                y++;
                cout<<y->F<<" "<<y->S<<endl;
                return 0;
            }
        }
    }
    cout<<"NO"<<endl;
}

D問題

(UnionFindによる操作はほぼ定数とみなして計算量を表記しています。)

全体から部分集合を選ぶというイメージで行うと沼にハマってしまうかもしれません。ここでは、二つの点の距離が$2^d$かつ最大で$2 \times 10^9$なので、$d=$0~30のみを調べれば良いことに注目します。

この時、それぞれの$d$について最大の部分集合を求めることを考えます。また、ある点$x$に注目した時$2^d$だけ離れている点は$x-2^d,x+2^d$であり、全ての点をsetで管理すれば離れている点が存在するかを$O(\log{n})$で確かめることができます(任意の点で調べれば$O(n \log{n})$)。したがって、UnionFindを用いて間が$2^d$離れている点どうしをつないでいくことで下図のような点の部分集合がいくつかできます。

IMG_0642.JPG

また、上図の部分集合に含まれる点は全て題意を満たすような部分集合に含まれるとしたいのですが、高々3つの点までしか含めることができません。なぜなら、3つより点が大きい場合は距離が2の冪乗にならない点の組が必ず含まれるからです。

以上より、$d=$0~30それぞれでUnionFindを行って(集合内の要素を昇順に並べると座標の間隔が$2^d$である)素集合系を求め、要素数が三以上の集合が少なくとも一つある場合はその集合を昇順で並べた時に隣合う三つの点を出力し、(前述の条件を満たさず)要素数が2のものが少なくとも一つある場合はその集合に含まれる二点を出力し、それ以外の場合は適当な一点を出力すれば良いです。

また、点同士をuniteする場合にindexを指定したかったので値に対してのインデックスを与えるmapとしてindを用意し、出力の際にインデックスから要素の値に直すためのvectorとしてvalを用意しました。また、UnionFindで得られた集合はインデックスの昇順になっているので、値の昇順と一致させるためにvalを昇順に直してからindに格納する必要があります。(昇順のソートをしていなかったのに気づかず、デバッグに数時間かかりました…。考察段階では気づいていたミスなので、焦ったときは自分の考察と照らし合わせるようにしたいです。)

D.cc
//デバッグ用オプション:-fsani

//コンパイラ最適化
#pragma GCC optimize("Ofast")

//インクルードなど
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

//マクロ
//forループ
//引数は、(ループ内変数,動く範囲)か(ループ内変数,始めの数,終わりの数)、のどちらか
//Dがついてないものはループ変数は1ずつインクリメントされ、Dがついてるものはループ変数は1ずつデクリメントされる
//FORAは範囲for文(使いにくかったら消す)
#define REP(i,n) for(ll i=0;i<ll(n);i++)
#define REPD(i,n) for(ll i=n-1;i>=0;i--)
#define FOR(i,a,b) for(ll i=a;i<=ll(b);i++)
#define FORD(i,a,b) for(ll i=a;i>=ll(b);i--)
#define FORA(i,I) for(const auto& i:I)
//xにはvectorなどのコンテナ
#define ALL(x) x.begin(),x.end() 
#define SIZE(x) ll(x.size()) 
//定数
#define INF 1000000000000 //10^12:∞
#define MOD 1000000007 //10^9+7:合同式の法
#define MAXR 100000 //10^5:配列の最大のrange
//略記
#define PB push_back //挿入
#define MP make_pair //pairのコンストラクタ
#define F first //pairの一つ目の要素
#define S second //pairの二つ目の要素

//以下、素集合と木は同じものを表す
class UnionFind {
public:
    vector<ll> parent; //parent[i]はiの親
    vector<ll> siz; //素集合のサイズを表す配列(1で初期化)
    map<ll,vector<ll>> group;//素集合ごとに管理する連想配列(keyはそれぞれの素集合の親、valueはその素集合の要素の配列)
    ll n;//要素の個数

    //コンストラクタ
    UnionFind(ll n_):parent(n_),siz(n_,1),n(n_){ 
        //全ての要素の根が自身であるとして初期化
        for(ll i=0;i<n;i++){parent[i]=i;}
    }

    //データxの属する木の根を取得(経路圧縮も行う)
    ll root(ll x){
        if(parent[x]==x) return x;
        return parent[x]=root(parent[x]);//代入式の値は代入した変数の値なので、経路圧縮できる
    }

    //xとyの木を併合
    void unite(ll x,ll y){
        ll rx=root(x);//xの根
        ll ry=root(y);//yの根
        if(rx==ry) return;//同じ木にある時
        //小さい集合を大きい集合へと併合(ry→rxへ併合)
        if(siz[rx]<siz[ry]) swap(rx,ry);
        siz[rx]+=siz[ry];
        parent[ry]=rx;//xとyが同じ木にない時はyの根ryをxの根rxにつける
    }

    //xとyが属する木が同じかを判定
    bool same(ll x,ll y){
        ll rx=root(x);
        ll ry=root(y);
        return rx==ry;
    }

    //xの素集合のサイズを取得
    ll size(ll x){
        return siz[root(x)];
    }

    //素集合をそれぞれグループ化
    void grouping(){
        //経路圧縮を先に行う
        REP(i,n)root(i);
        //mapで管理する(デフォルト構築を利用)
        REP(i,n)group[parent[i]].PB(i);
    }

    void clear(){
        for(ll i=0;i<n;i++){parent[i]=i;}
        siz=vector<ll>(n,1);
        group.clear();
    }
};

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    ll n;cin>>n;
    set<ll> x;
    //値に対してのind
    map<ll,ll> ind;
    //indに対しての値
    vector<ll> val(n);
    REP(i,n){
        ll y;cin>>y;
        x.insert(y);
        val[i]=y;
    }
    sort(ALL(val));
    REP(i,n){
        ind[val[i]]=i;
    }
    vector<ll> v(1,0);
    pair<ll,vector<ll>> ans=MP(1,v);
    UnionFind uf(n);
    REP(i,31){
        uf.clear();
        FORA(j,val){
            if(x.find(j+(1LL<<i))!=x.end()){
                uf.unite(ind[j],ind[j+(1LL<<i)]);
            }
        }
        uf.grouping();
        for(auto j=uf.group.begin();j!=uf.group.end();j++){
            if(SIZE(j->S)>ans.F){
                //3は超えない
                //sortされてない(valは)はー???????
                if(ans.F==1){
                    if(SIZE(j->S)==2){
                        vector<ll> w(2);
                        w={j->S[0],j->S[1]};
                        ans=MP(2,w);
                    }else{
                        cout<<3<<endl;
                        cout<<val[j->S[0]]<<" "<<val[j->S[1]]<<" "<<val[j->S[2]]<<endl;
                        return 0;
                    }
                }
                if(ans.F==2){
                    if(SIZE(j->S)>2){
                        cout<<3<<endl;
                        cout<<val[j->S[0]]<<" "<<val[j->S[1]]<<" "<<val[j->S[2]]<<endl;
                        return 0;
                    }
                }
            }
        }
    }
    cout<<ans.F<<endl;
    REP(i,ans.F){
        if(i==ans.F-1){
            cout<<val[ans.S[i]]<<endl;
        }else{
            cout<<val[ans.S[i]]<<" ";
        }
    }
}

E問題

(実装がキツかったです。解説は可及的速やかにあげます。)

E.py
n=[int(i) for i in input()]
l=len(n)
#長さが2の場合も
if len(n)==1:
    print(-1)
    exit()
#これだけの場合
if n in [[2,5],[7,5],[5,0]]:
    print(0)
    exit()
if n in [[5,2],[5,7]]:
    print(1)
    exit()
inf=10**12
#一番左にある場合はめんどいので場合分けしちゃう(逆にする)
#00も場合分け
def calc1(x,y):
    global n,l,inf
    m=n[::-1]
    if x not in n or y not in n:
        #print(x,y,0)
        return inf
    ix,iy=m.index(x),m.index(y)
    #一番右にない場合
    if max(ix,iy)<l-1:
        if iy<ix:
            #print(x,y,1)
            return iy+(ix-1)
        else:
            #print(x,y,2)
            return iy+(ix-1)+1
    else:
        ret=0
        #print(ix,iy)
        if ix==l-1:
            #右から探して左を見る(0かつ他の選択するやつでない)
            for i in range(l-2,-1,-1):
                if m[i]!=0 and i!=iy:
                    break
            else:
                #print(x,y,3)
                return inf
            if i<iy:
                iy-=1
                ret+=(l-1-i)
                ix-=1
                ret+=(iy+(ix-1))
                #print(x,y,4,ret)
                return ret
            else:
                ret+=iy
                ret+=(l-1-i)
                ix-=1
                ret+=(ix-1)
                #print(x,y,5,ret)
                return ret
        else:
            #右から探して左を見る(0かつ他の選択するやつでない)
            #他の選択するやつだけの可能性(最初に排除)
            #外でやる
            for i in range(l-2,-1,-1):
                if m[i]!=0 and i!=ix:
                    break
            else:
                #print(x,y,6)
                return inf
            if i<ix:
                iy-=1
                ret+=(l-1-i)
                ix-=1
                ret+=(iy+(ix-1)+1)
                #print(x,y,7,ret)
                return ret
            else:
                ret+=iy
                ret+=(l-1-i)
                ix-=1
                ret+=((ix-1)+1)
                #print(x,y,8,ret)
                return ret
#00の時
def calc2(x,y):
    #一番右には絶対ない
    global n,l,inf
    if n.count(0)<2:
        return inf
    m=[l-1-i for i in range(l) if n[i]==0][::-1]
    return m[0]+m[1]-1

ans=min([calc1(2,5),calc1(7,5),calc1(5,0),calc2(0,0)])
#print([calc1(2,5),calc1(7,5),calc1(5,0),calc2(0,0)])
print(-1 if ans==inf else ans)

F問題

今回は飛ばします

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

ラズパイで今後やりたいことをメモしていく

この記事は

勉強と興味のためにRaspberryPi 3を買ったはいいが、やってみたいことだらけなのに触る時間がなくやりたいことがどんどん増えていく。
ので、このページに備忘録としてまとめていく。
そういう記事。

やりたいこと

IoT関係

  • 自室の4箇所+窓の外の1箇所に温度センサー(温湿度?)を設置し、一定時間ごとに温度を計測してログをためこむ。最終的にspreadsheetやwebブラウザなどでグラフ化して表示する

参考
Raspberry Piのセンサーで検出したデータをPythonでテキストログに出力させる - Qiita
CO2センサを部屋につけてログを取る - Qiita
ログローテーションの設定方法 - Qiita
Raspberry Pi(ラズパイ)で温度測定してログを取ってみる - WICの中から

  • 赤外線LEDとリモコン受信つきの照明を使って、部屋の照明を音声認識やtwitterからの指示でONOFFできるようにする。

参考
Raspberry Pi Zero で赤外線リモコンを作る - Qiita
Raspberry Pi3で赤外線リモコンを作る - なになれ
ラズパイとTwitter(ツイッター)を連携 - Raspberry Π
Raspberry PiからTwitterへ情報発信!(1) Twitter API編 | Device Plus - デバプラ

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

置換する方法

pythonの文字列に変数を埋め込む方法

・ここでは、2つ紹介します。

・1,f-string
・2,format()

f-string

先頭にfをつけて定義する文字列

a="apple"
f"I like an {a}"#変数の値で置換
実行結果
I like an apple

format()

文字列内の{}が、メソッドstr.format()の引数に渡した値に置換される。

a="apple"
"I like an {}".format(a)
実行結果
I like an apple

複数の引数を置換する

・1.f-string

a="apple"
b="orange"
f"I like an {a} and an {b}"
実行結果
I like an apple and an orange

2.format()

a="apple"
b="orange"
"I like an {} and {}".format(a,b)
実行結果
I like an apple and an orange
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

pythonの文字列に変数を埋め込む方法

pythonの文字列に変数を埋め込む方法

・ここでは、2つ紹介します。

・1,f-string
・2,format()

f-string

先頭にfをつけて定義する文字列

a="apple"
f"I like an {a}"#変数の値で置換
実行結果
I like an apple

format()

文字列内の{}が、メソッドstr.format()の引数に渡した値に置換される。

a="apple"
"I like an {}".format(a)
実行結果
I like an apple

複数の引数を置換する

・1.f-string

a="apple"
b="orange"
f"I like an {a} and an {b}"
実行結果
I like an apple and an orange

2.format()

a="apple"
b="orange"
"I like an {} and {}".format(a,b)
実行結果
I like an apple and an orange
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VS CodeでFortranをデバッグ設定までやってみる。【Win10】

初めに

FortranをWindowsでビルド・デバッグする場合は、どういうツールを組み合わせればいいのか調べた。
デバッグでブレークポイントに止まらない問題でやや詰まったが解決。
必要最小限の構成で考えている。

使用するツールや環境

・Windows 10
・Visual Studio Code
 ※エクステンションは2つ:Modern Fortran、C/C++
・gfortran(フリーのコンパイラ)

1.Fortranコンパイラの準備

TDM-GCCから、「tdm64-gcc-***.exe」をダウンロード
 ※gccだけど、gfortranも入っている。
・インストールする。fortranのチェックを忘れずに。
 image.png
・インストールされたか確認。以下2つのファイルが重要。
 image.png

2.VS Codeの準備

※VS Codeインストールは割愛。
・画面一番左の「拡張機能」から「Modern Fortran」を検索してインストール。
 ※自動的に「C/C++」もインストールされる。
・インストールされた「Modern Fortran」を右クリック→拡張機能の設定
・gfortranの場所を指定
 image.png
・実行→構成を開く→C++(GDB/LLDB)
 ※Fortranだけど、C++を選ぶ。というか、そもそもFortranがない。
 下のように、"program"と"miDebuggerPath"を自分の環境に合わせて設定。他はそのままでいいかな。

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) 起動",
            "type": "cppdbg",
            "request": "launch",
            "program": "${workspaceFolder}/a.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "C:/TDM-GCC-64/bin/gdb.exe",
            "setupCommands": [
                {
                    "description": "gdb の再フォーマットを有効にする",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

3.Fortranのビルド

・ビルド作業はVS Code上ではなく、コマンドプロンプトで実行。
・今回はデバッグをするためには、必ずオプション 「-g」を付ける。

C:\Temp>gfortran -g fortran_program.f90

・exeファイルが作成される。
image.png

4.VS Codeによるデバッグ

・VS Codeでフォートランファイルを開き、行数の左にデバッグポイントを打つ
image.png
・実行→デバッグの実行。
 すると、ちゃんとデバッグポイントで止まる!
image.png

終わり

ビルド時にオプション必要というのがやや詰まりましたが、できて良かった。
学生時代はprint文によるデバッグしかできなかったから、Fortranでもデバッグできるんだなぁと感慨深いです。
宜しければお使いください。

「gfortran -g」がVS Codeから実行できるかどうかは、わからないです。どうやるんだろ、多分簡単だろうけどここまでにしておきます。

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

macOS上にpydicomをセットアップするためのメモ

公式のセットアップ手順 はDebian系のLinux環境を前提としている部分があるため、macOS上でセットアップした際の手順を残しておく

環境情報

  • macOS Catalina 10.15.6
  • Python 3.8.5 on pyenv 1.2.20

※Anacondaは利用しない

セットアップ手順

pydicom

$ pip install pydicom

Optional libraries

$ pip install numpy
$ pip install pillow

CharPyLS

$ pip install cython
$ pip install git+https://github.com/Who8MyLunch/CharPyLS

GDCM

Anacondaなしでは少々手間のかかる部分
Installing the Python GDCM bindings without Conda を参考に、Ubuntu/Debian向けの記述をmacOS向けにアレンジする

$ brew install gdcm

# インストールされた関連ファイルの場所を確認する
$ find /usr -name gdcm.py
/usr/local/lib/python3.8/site-packages/gdcm.py
/usr/local/Cellar/gdcm/3.0.7_1/lib/python3.8/site-packages/gdcm.py

$ find /usr -name "libgdcmCommon.*"
/usr/local/lib/libgdcmCommon.3.0.dylib
/usr/local/lib/libgdcmCommon.3.0.7.dylib
/usr/local/lib/libgdcmCommon.dylib
/usr/local/Cellar/gdcm/3.0.7_1/lib/libgdcmCommon.3.0.dylib
/usr/local/Cellar/gdcm/3.0.7_1/lib/libgdcmCommon.3.0.7.dylib
/usr/local/Cellar/gdcm/3.0.7_1/lib/libgdcmCommon.dylib

# pyenv環境へ関連ファイルをコピー
$ cd ~/.pyenv/versions/3.8.5/lib/python3.8/site-packages/
$ cp /usr/local/lib/python3.8/site-packages/gdcm.py .
$ cp /usr/local/lib/python3.8/site-packages/gdcmswig.py .
$ cp /usr/local/lib/python3.8/site-packages/_gdcmswig* .
$ cp /usr/local/lib/libgd* .

Pythonのプロンプト上で import gdcm してエラーが出なければOK

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

YOLOを利用した物体検出(python)(【高等学校情報科 情報Ⅱ】教員研修用教材)

はじめに

YOLO(You Only Look Once)とは、その名の通り人間のように一度見ただけで物体の認識・検出をしてしまうという画期的なアルゴリズムです。
従来の手法に比べて、処理が高速であり物体と背景の認識の区別に強く、汎用化しやすいという特徴を持っています。
今回は、教材内でTiny YOLOとRを使って物体検出を行っている個所を、YOLOとpythonを使用して写真上の物体の検出を行っていきます。

教材

高等学校情報科「情報Ⅱ」教員研修用教材(本編):文部科学省
第3章 情報とデータサイエンス 後半 (PDF:7.6MB)

環境

教材内で取り上げる箇所

学習18 テキストマイニングと画像認識:「3.TinyYOLOを利用した物体検出」

pythonでの実装例と結果

今回は、darknetをgithubのリポジトリからclone(コピー)し、YOLOv3の学習済み重みデータyolov3.weightsを使用して、画像認識を行います。
今回はpythonでの実装例と書いてありますが、darknetでYOLOv3を動かすためコマンドの実行を中心に行い、なるべく自前でコーディングしないような形で物体検出できるようにしていきたいと思います。

!git clone https://github.com/pjreddie/darknet

実行結果は以下のようになりました

Cloning into 'darknet'...
remote: Enumerating objects: 5913, done.
remote: Total 5913 (delta 0), reused 0 (delta 0), pack-reused 5913
Receiving objects: 100% (5913/5913), 6.34 MiB | 9.93 MiB/s, done.
Resolving deltas: 100% (3918/3918), done.

git cloneできたので、darknetディレクトリ配下に移動し、makeを実行します。

import os

os.chdir('darknet')
!make

makeが完了したら、YOLOv3の学習済み重みデータyolov3.weightsを同じディレクトリにダウンロードしておきます。今回はwgetコマンドを使用しました。

!wget https://pjreddie.com/media/files/yolov3.weights

本題の、YOLOで物体検出をさせたいと思いますが、今回はdataディレクトリ内のgiraffe.jpgを使用して、物体検出をしてみたいと思います。

!./darknet detect cfg/yolov3.cfg yolov3.weights data/giraffe.jpg
layer     filters    size              input                output
    0 conv     32  3 x 3 / 1   608 x 608 x   3   ->   608 x 608 x  32  0.639 BFLOPs
    1 conv     64  3 x 3 / 2   608 x 608 x  32   ->   304 x 304 x  64  3.407 BFLOPs
    2 conv     32  1 x 1 / 1   304 x 304 x  64   ->   304 x 304 x  32  0.379 BFLOPs
    3 conv     64  3 x 3 / 1   304 x 304 x  32   ->   304 x 304 x  64  3.407 BFLOPs
  103 conv    128  1 x 1 / 1    76 x  76 x 256   ->    76 x  76 x 128  0.379 BFLOPs
  104 conv    256  3 x 3 / 1    76 x  76 x 128   ->    76 x  76 x 256  3.407 BFLOPs
  105 conv    255  1 x 1 / 1    76 x  76 x 256   ->    76 x  76 x 255  0.754 BFLOPs
  106 yolo
Loading weights from yolov3.weights...Done!
data/giraffe.jpg: Predicted in 19.677707 seconds.
giraffe: 98%
zebra: 98%

キリン(giraffe)とシマウマ(zebra)が検出できました。
実際の検出された画像を見てみましょう。

from IPython.display import Image

Image("predictions.jpg")

無題01.png

うまく検出できました。

ソースコード

https://gist.github.com/ereyester/46a25e70c866c581320a66a77153aa2d

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

内包表記

 内包表記

Pythonには内包表記と呼ばれる記法があります。
内包表記とは、リストコンプリヘンションと同じ意味です。

三種類ありますが、リストの内包表記から説明していきます。
リストオブジェクトとループを利用して新たなリストを生成する構文。
他の言語にはあまりない独特な構文ですが、慣れると非常に便利そして非常に高速というのがメリット

内包表記の基本的な書き方
[式 for 変数 in シーケンス]

リスト内包表記を使用しない場合

num_list = [1, 2, 3, 4, 5]
number_list = []
for num in num_list:
 >   new_num = num * 2
  >  number_list.append(new_num)
 
print(number_list)

[2, 4, 6, 8, 10]

リスト内包表記を使用する場合

num_list = [1, 2, 3, 4, 5]
number_list = [num * 2 for num in num_list ]
print(number_list)

[2, 4, 6, 8, 10]

要はリスト内包表記を使うことによって、短くまとまった記載にできるということです。

以下のように表記することでif文を使用することも可能です。

[式 for 変数 in シーケンス if リスト要素に対する条件]

num = “1, 2, 3, 4, a,5"
number = [int(s) for s in num.split() if s.isdigit()]
print(number)

[1, 2, 3, 4, 5]

ディクショナリ内包表記

リスト内包表記と同じく、辞書型の要素をまとめて処理することができる。
使い方はリスト内包表記と同じ。

内包表記の基本的な書き方
{キー:for 変数 in シーケンス (if 条件)}

subject = {‘kokugo’: 75, ‘suugaku’: 64, ‘eigo’: 92}
num = {point: str(n) + ‘点’ for point, n in subject.items()}
print(num)
{‘kokugo’: 75点, ‘suugaku’: 64点, ‘eigo’: 92点}

セット内包表記
集合内包表記とも呼ばれます

セット内包表記の基本的な書き方
{式 for 変数 in シーケンス (if 条件)}

num_set = {number for number in range(19) if number % 3 == 0}
print(num_set)
{0, 3, 6, 9, 12, 15, 18}

イテレータとは、

集合の要素に準々にアクセスする場合に使用するインターフェースで、コレクションを繰り返し使うためにあるものです。
リスト、セット、マップなど複数の種別がありますが、イテレータはそれらにアクセスするための仕組みです。
例えば、javaでは、Set#iteratorメソッドIteratorを取りだし、順番に要素を取得しますが、Pythsonではイテレータオブジェクトを使用します。
イテレータを使用することで、要素へのアクセスを記述することが可能です。
Pythonのfor文においてはiterableを範囲にとって、暗黙的にiteratorを利用する点を指して内部イテレータと呼ばれる場合もある。

※インターフェース
コンピュータで、異なる機器・装置のあいだを接続して、交信や制御を可能にする装置やソフトウェアのこと

※コレクション
リストやSetのように、複数の要素を扱うオブジェクトです。

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

文字列まとめ1

文字列

・pythonには、文字列を扱うデータの型としてstr型がある

文字列の演算

a="note"
b="book"
a+b
実行結果
"notebook"

for文を用いて1文字ずつ要素を出力

for a in "book":
    print(i)
実行結果
b
o
o
k
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

kivy JsonStore でUnicodeDecodeError を解決する

問題のエラー

Kivyでstorageモジュールを使ってjsonを扱う際、以下のようなエラーに遭遇した。
※環境はpython3.8 ですが、大した差異はないと思います。

   File "C:\programing\project\hoge\main.py", line 33, in _init_load
     self.store = JsonStore('test.json', )
   File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\kivy\storage\jsonstore.py", line 29, in __init__
     super(JsonStore, self).__init__(**kwargs)
   File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\kivy\storage\__init__.py", line 134, in __init__
     self.store_load()
   File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\kivy\storage\jsonstore.py", line 43, in store_load
     data = fd.read()
 UnicodeDecodeError: 'cp932' codec can't decode byte 0x82 in position 85: illegal multibyte sequence

親の顔より見たUnicodeDecodeErrorですね。日本語が入ってることが原因ですかね?
このエラーは基本的にopen関数の引数にencoding='utf-8'を追加すれば解決できます。
というわけでstorageモジュールの中でjsonファイルを開く部分をサクッと変更します。
31行目に読み込みを行う関数があるので、そこのopen部分にキーワード引数を追加します。

kivy/storage/jsonstore.py
def store_load(self):
        if not exists(self.filename):
            folder = abspath(dirname(self.filename))
            if not exists(folder):
                not_found = IOError(
                    "The folder '{}' doesn't exist!"
                    "".format(folder)
                )
                not_found.errno = errno.ENOENT
                raise not_found
            return
        with open(self.filename, encoding='utf-8') as fd:    # ココ
            data = fd.read()
            if len(data) == 0:
                return
            self._data = loads(data)

結果

正常に読み込むことに成功しました。
日本語はややこしいですね。。。

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

Missing key(s) in state_dict: "bert.embeddings.position_ids".

transformers3でそれ以前のモデルをロードするとこのエラーがでる.
対策としては,ダウングレードすれば良い

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

E資格取得を目指して〜その1〜

E資格取得にむけて、不定期で投稿しています。

状況

  • ゼロから作るDeep Learningの1章から7章まで読了、8章を途中まで
  • ゼロから作るDeep Learningの第2章を学習中
  • JDLA認定プログラムの講座を適宜受講。課題への着手。

所感

エンジニアの幅を広げるために取り組みを開始しましたが、思った以上に難しい。
なんとなく(G検定持ってない)理解している状態からスタートしたのでまずは全体感を理解するためにゼロつくシリーズをやってます。
1ヶ月前は講座の内容に合わせ計画を作ってみましたが、あまり意味がないことに気付き、まず全部やってみる。
後から個々の細かい部分を肉付けしていくやり方にしています。(今のところ、マッチしていると思っている)

その他

もう少し余裕が出てくるくらいの習熟度に慣れたら、学習内容を自分なりの理解をアウトプットすることにもチャレンジしたいと思ってます。
ただ、今はまずは内容を把握するところから・・・

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

機械学習概論 メモ書き

Aidemy 2020/9/23

はじめに

 こんにちは、んがょぺです!文系大学生ですが、AI分野に興味が湧いたのでAI特化型スクール「Aidemy」に通い、勉強しています。ここで得られた知識を皆さんと共有したいと思い、Qiitaでまとめています。以前のまとめ記事も多くの方に読んでいただけてとても嬉しいです。ありがとうございます!
 今回は機械学習概論について、重要なところのメモ書きを行なって行こうと思います。

機械学習概論1 要点

・機械学習には「教師あり学習」「教師なし学習」「強化学習」がある。
・教師あり学習は学習データと正解(教師)データを与えて正解するまで思考する方法。最頻。
・教師なし学習は学習データのみを与え、コンピュータ自身が規則性を見つける方法。
・強化学習は、行動主体が得られる利益(報酬)を最大化するよう思考し続ける方法。

機械学習概論2 要点

教師あり学習の手順:データ収集→データクレンジング→学習→テストデータでチェック→実装

教師あり学習の実践1:ホールドアウト法:データを学習データとテストデータに分けて使用。train_test_split()関数を使う。
X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=テストデータの割合,random_state=0)
*Xは正解ラベル以外、yは正解ラベル。trainが学習データ、testがテストデータ。random_stateはテストデータを選ぶseed。

教師あり学習の実践2:k-分割交差検証:データをk分割しそのうち1つをテストデータとして使用。テストデータを毎回変えて計k回検証してその平均性能を算出する。(ex 20個のデータなら、19個を学習データ、1個をテストデータとし、計20回検証する)

過学習:学習の精度が高すぎて適切に抽象化できず、未知のデータに対応できない状態。
ドロップアウト:過学習回避の手段。明らかな例外を無視する。
アンサンブル学習:複数のモデルに学習させ、結果の平均を取ることで精度を高める。

機械学習概論3 要点

混同行列:モデルの精度を評価する際に使われる表。結果を「真陽性」「偽陽性」「真陰性」「偽陰性」に分類する。「真偽」は正解したかどうか、「陽陰」はモデルの解答を示す。(つまり「偽陽性」なら、モデルはTrueと解答したが、答えはFalseだったということ)

・混同行列の実装:以下のように記述(「y_true」には[正解のリスト]を、「y_pred」には[モデルの解答のリスト]を与える。)

from sklearn.metrics import confusion_matrix
#「正解」と「解答」をリストで定義(0が陽性、1が陰性)
y_true=[1,1,1,1,1,1]
y_pred=[1,1,1,0,0,0]

confmat = confusion_matrix(y_true, y_pred)
#[[0 0]   #[[真陽性 偽陰性]
# [3 3]]  # [偽陽性 真陰性]]

正解率:全解答のなかで、「真」だった割合。(真陽性+真陰性/全体)
適合率/精度:「陽」と解答したもののうち、「真」だった割合。(真陽性/真陽性+偽陽性)
再現率:「実際の陽」のうち、「真」だった割合。(真陽性/真陽性+偽陰性)
F値:適合率と再現率の調和平均(2*適合率*再現率/適合率+再現率)
*これらは全て0〜1で表され、1に近い方が性能が良いと言える。

・上記評価指標を実装:関数をインポートして、それぞれ「y_true」と「y_pred」を引数として与えることで計算してくれる。

#precision_score:適合率, recall_score:再現率, f1_score:F値 のインポート
from sklearn.metrics import precision_score,recall_score,f1_score

y_true=[0,0,1,1]
y_pred=[0,1,1,1]
#F値の出力
print("F1".format(f1_score(y_true,y_pred)))
# 0.666666

PR曲線:縦軸に適合率(Precision)、横軸に再現率(Recall)をとったグラフ。
適合率と再現率はトレードオフの関係であり、場合によってどちらに重きを置くかを考える必要がある。特にこだわりがない時は、F値や、PR曲線においてPとRが一致する点(ブレークイーブンポイント(BEP))を用いると良い。

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

データクレンジング3

Aidemy 2020/9/23

はじめに

 こんにちは、んがょぺです!文系大学生ですが、AI分野に興味が湧いたのでAI特化型スクール「Aidemy」に通い、勉強しています。ここで得られた知識を皆さんと共有したいと思い、Qiitaでまとめています。以前のまとめ記事も多くの方に読んでいただけてとても嬉しいです。ありがとうございます!
 今回は、データクレンジングの3つ目の投稿になります。どうぞよろしくお願いします。

*本記事は「Aidemy」での学習内容を「自分の言葉で」まとめたものになります。表現の間違いや勘違いを含む可能性があります。ご了承ください。

今回学ぶこと
・画像データについて
・OpenCV(画像を扱うライブラリ)でできること

1 画像データについて

コンピュータ上の「色」

・データ上、色はRGBデータとして、赤、緑、青で表現される(255,0,255 など)。
・画像は点(ピクセル)の集まりでできている。
・1つのピクセルを表すための色要素の数をチャンネル数という(RGBなら三色なので「3」)。

画像データの種類

・BMP 圧縮不可でサイズ大
・JPG 高圧縮可だが解凍不可
・PNG 圧縮解凍可 透過処理可
・GIF アニメをサポート 透過処理可

2 OpenCV

OpenCVで画像の読み込み、表示

・OpenCVは画像を扱う際に使われるライブラリ。cv2をインポートして使う。
cv2.imread("ファイル名")で読み込み
cv2.imshow("ウィンドウ名",読込済みの画像データ)で表示(ウィンドウ名は自由に付けてOK)

import cv2
#sample.jpgというファイル名の画像を読み込む
img = cv2.imread("./4050_data_cleansing_data/sample.jpg")
#ウィンドウ名をwindowにしてimgを出力
cv2.imshow("window",img)

画像の作成と保存(単色)

・画像を作成するには、np.array()関数(NumPyの行列を作る関数)を使う。また、range()を変数なしで多重ループさせて、縦横のピクセル情報(順番は青緑赤)を設定する。
・np.array([[[B,G,Rの値]for _ in range(横のサイズ)] for_ in range(縦のサイズ)],dtype="uint8")
*uint8型とは、int型の中でも0〜255の値しか取らないもの。
・画像の保存はcv2.imwrite("ファイル名",画像)で行う。

#(512*512)の一面緑(0,255,0)の画像を作成
img = np.array([[[0,255,0]for _ in range(512)]for _ in range(512)],dtype="uint8")
#imgを保存(ファイル名は「green.img」)
cv2.imwrite("green.img",img)

トリミング(切り取り)、リサイズ(拡大・縮小)

・トリミングは画像データ[y軸始点:y軸終点,x軸始点:x軸終点] 0は左上
・リサイズはcv2.resize(画像データ,(幅,高さ))で変更。

#トリミング用に画像サイズを取得(高さ,幅,色数)
size = img.shape #(1000,667,3)
#高さが1/2、幅が1/3になるようトリミング(始点を指定せず、余りが出ないように割る)
new_img=img[:size[0]//2,:size[1]//3]
#高さ2倍、幅3倍にリサイズ
new_img=cv2.resize(new_img,(new_img.shape[0]*2,new_img.shape[1]*3))

回転・反転

・画像の回転にはアフィン変換という変換が必要であり、変換行列を取得しなければならない。
cv2.getRotationMatrix2D(画像の中心座標,回転角度,拡大縮小の倍率)で変換行列を取得する。
・そしてcv2.warpAffine(画像,変換行列,出力サイズ)で実際に回転させる。

・反転はcv2.flip(画像,flipCode)で行う。第二引数には、「0」と指定すれば上下反転、正の数を指定すれば左右反転、負の数を指定すれば上下左右反転となる。

#変換行列の取得(倍率2倍で90度回転)
mat=cv2.getRotationMatrix(tuple(np.array([img.shape[1],img.shape[0]])/2),90,2.0)
#実際に回転
cv2.warpAffine(img,mat,img.shape[::-1][1:3])

色調変換・色反転

・今までは「BGR色空間」しか使っていなかったが、OpenCV上には他の色空間も存在する。
cv2.cvtColor(画像,変換コード)で色空間を変更できる。変換コードは、例えばBGR色空間をLabという色空間に変換したい時は「cv2.COLOR_BGR2LAB」となる。

・色反転は、各ピクセルを順番に取り出して、値xについて「255-x」とすることで行うこともできるが、
cv2.bitwise_not(画像)で簡単に反転できる。

#imgの色空間をLabに変更
c_img=cv2.cvtColor(img,cv2.COLOR_BGR2LAB)
#imgの色を反転
r_img=cv2.bitwise_not(img)

OpenCVの応用

閾値処理

・画像の容量を小さくするために、色のデータを「白」「黒」のみにする。
cv2.threshold(画像,閾値,最大値(濃度),閾値処理の種類)
*閾値より大きいか小さいかでデータを分ける。その分け方を第4引数に指定する。

#閾値127、最大値255、種類はcv2.THRESH_TOZERO(閾値以下は0、閾値以上は変更なし)
new_img=cv2.threshold(img,127,255,cv2.THRESH_TOZERO)

マスキング

・白黒のマスク画像を別途準備し、cv2.bitwise_and()関数で元の画像と合わせると、元の画像のうち、マスク画像の白の部分しか出力されなくなる。これを「マスキング」という。
cv2.bitwise_and(元の画像1,元の画像2(マスク時に使用),mask=マスク用の白黒画像)

#マスク用画像を読み込み(チャンネル数1の白黒画像)、リサイズして大きさを揃える。
mask=cv2.imread("./4050_cleansing_data/mask.png", 0)
mask=cv2.resize(mask,(img.shape[1],img.shape[0]))
#マスキングする
cv2.bitwise_and(img,img,mask=mask)

画像をぼかす

・画像をぼかすには「ガウシアンフィルタ」を使う。1ピクセルの周りn*n(nは奇数)を平均化してぼかす。
cv2.GaussianBlur(画像,(n*nの値),標準偏差)
*標準偏差は通常0。n*nの値と標準偏差が大きくなるほどぼかしが強くなる。

#(51*51)のぼかしを行う
new_img=cv2.GaussianBlur(img,(51,51),0)

ノイズ(荒)の除去

cv2.fastNlMeansDenoisingColored(画像)で行う。カラー画像でなければ関数名の「Colored」は抜いて良い。(NlMeansとは、Non-local Means Filterというノイズ除去のフィルターを指す、Denoisingはノイズを除去するという意味)

new_img=cv2.fastNlMeansDenoisingColored(img)

収縮・膨張

・ノイズ処理の別の方法として、画像を一度収縮してから再度膨張させる方法がある。この方法は「閾値処理」のノイズ除去に使われることが多い。
cv2.dilate(画像,フィルタ) で膨張、cv2.erode(画像,フィルタ) で収縮。

#imgを閾値処理
new_img=cv2.threshold(img,127,255,cv2.THRESH_BYNARY)
#フィルタの定義
filter=np.array([[0,1,0],[1,0,1],[0,1,0]],np.uint8)
#収縮して膨張
new_img=cv2.erode(new_img,filter)
new_img=cv2.dilate(new_img,filter)

まとめ

・画像データはOpenCVで処理する。
・OpenCVでは、imread()で画像読み込み、imshow()で画像出力、resize()でリサイズ、flip()で反転、bitwise_notで色反転ができる。その他、画像作成やトリミング、回転などもできる。
・応用として、threshold()で閾値処理、bitwise_and()でマスキング、GaussianBlur()でぼかしを行う。その他、ノイズを除去することもできる。

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

データクレンジング3 OpenCVの利用と画像データの前処理

Aidemy 2020/9/23

はじめに

 こんにちは、んがょぺです!文系大学生ですが、AI分野に興味が湧いたのでAI特化型スクール「Aidemy」に通い、勉強しています。ここで得られた知識を皆さんと共有したいと思い、Qiitaでまとめています。以前のまとめ記事も多くの方に読んでいただけてとても嬉しいです。ありがとうございます!
 今回は、データクレンジングの3つ目の投稿になります。どうぞよろしくお願いします。

*本記事は「Aidemy」での学習内容を「自分の言葉で」まとめたものになります。表現の間違いや勘違いを含む可能性があります。ご了承ください。

今回学ぶこと
・画像データについて
・OpenCV(画像を扱うライブラリ)でできること

1 画像データについて

コンピュータ上の「色」

・データ上、色はRGBデータとして、赤、緑、青で表現される(255,0,255 など)。
・画像は点(ピクセル)の集まりでできている。
・1つのピクセルを表すための色要素の数をチャンネル数という(RGBなら三色なので「3」)。

画像データの種類

・BMP 圧縮不可でサイズ大
・JPG 高圧縮可だが解凍不可
・PNG 圧縮解凍可 透過処理可
・GIF アニメをサポート 透過処理可

2 OpenCV

OpenCVで画像の読み込み、表示

・OpenCVは画像を扱う際に使われるライブラリ。cv2をインポートして使う。
cv2.imread("ファイル名")で読み込み
cv2.imshow("ウィンドウ名",読込済みの画像データ)で表示(ウィンドウ名は自由に付けてOK)

import cv2
#sample.jpgというファイル名の画像を読み込む
img = cv2.imread("./4050_data_cleansing_data/sample.jpg")
#ウィンドウ名をwindowにしてimgを出力
cv2.imshow("window",img)

画像の作成と保存(単色)

・画像を作成するには、np.array()関数(NumPyの行列を作る関数)を使う。また、range()を変数なしで多重ループさせて、縦横のピクセル情報(順番は青緑赤)を設定する。
・np.array([[[B,G,Rの値]for _ in range(横のサイズ)] for_ in range(縦のサイズ)],dtype="uint8")
*uint8型とは、int型の中でも0〜255の値しか取らないもの。
・画像の保存はcv2.imwrite("ファイル名",画像)で行う。

#(512*512)の一面緑(0,255,0)の画像を作成
img = np.array([[[0,255,0]for _ in range(512)]for _ in range(512)],dtype="uint8")
#imgを保存(ファイル名は「green.img」)
cv2.imwrite("green.img",img)

トリミング(切り取り)、リサイズ(拡大・縮小)

・トリミングは画像データ[y軸始点:y軸終点,x軸始点:x軸終点] 0は左上
・リサイズはcv2.resize(画像データ,(幅,高さ))で変更。

#トリミング用に画像サイズを取得(高さ,幅,色数)
size = img.shape #(1000,667,3)
#高さが1/2、幅が1/3になるようトリミング(始点を指定せず、余りが出ないように割る)
new_img=img[:size[0]//2,:size[1]//3]
#高さ2倍、幅3倍にリサイズ
new_img=cv2.resize(new_img,(new_img.shape[0]*2,new_img.shape[1]*3))

回転・反転

・画像の回転にはアフィン変換という変換が必要であり、変換行列を取得しなければならない。
cv2.getRotationMatrix2D(画像の中心座標,回転角度,拡大縮小の倍率)で変換行列を取得する。
・そしてcv2.warpAffine(画像,変換行列,出力サイズ)で実際に回転させる。

・反転はcv2.flip(画像,flipCode)で行う。第二引数には、「0」と指定すれば上下反転、正の数を指定すれば左右反転、負の数を指定すれば上下左右反転となる。

#変換行列の取得(倍率2倍で90度回転)
mat=cv2.getRotationMatrix(tuple(np.array([img.shape[1],img.shape[0]])/2),90,2.0)
#実際に回転
cv2.warpAffine(img,mat,img.shape[::-1][1:3])

色調変換・色反転

・今までは「BGR色空間」しか使っていなかったが、OpenCV上には他の色空間も存在する。
cv2.cvtColor(画像,変換コード)で色空間を変更できる。変換コードは、例えばBGR色空間をLabという色空間に変換したい時は「cv2.COLOR_BGR2LAB」となる。

・色反転は、各ピクセルを順番に取り出して、値xについて「255-x」とすることで行うこともできるが、
cv2.bitwise_not(画像)で簡単に反転できる。

#imgの色空間をLabに変更
c_img=cv2.cvtColor(img,cv2.COLOR_BGR2LAB)
#imgの色を反転
r_img=cv2.bitwise_not(img)

OpenCVの応用

閾値処理

・画像の容量を小さくするために、色のデータを「白」「黒」のみにする。
cv2.threshold(画像,閾値,最大値(濃度),閾値処理の種類)
*閾値より大きいか小さいかでデータを分ける。その分け方を第4引数に指定する。

#閾値127、最大値255、種類はcv2.THRESH_TOZERO(閾値以下は0、閾値以上は変更なし)
new_img=cv2.threshold(img,127,255,cv2.THRESH_TOZERO)

マスキング

・白黒のマスク画像を別途準備し、cv2.bitwise_and()関数で元の画像と合わせると、元の画像のうち、マスク画像の白の部分しか出力されなくなる。これを「マスキング」という。
cv2.bitwise_and(元の画像1,元の画像2(マスク時に使用),mask=マスク用の白黒画像)

#マスク用画像を読み込み(チャンネル数1の白黒画像)、リサイズして大きさを揃える。
mask=cv2.imread("./4050_cleansing_data/mask.png", 0)
mask=cv2.resize(mask,(img.shape[1],img.shape[0]))
#マスキングする
cv2.bitwise_and(img,img,mask=mask)

画像をぼかす

・画像をぼかすには「ガウシアンフィルタ」を使う。1ピクセルの周りn*n(nは奇数)を平均化してぼかす。
cv2.GaussianBlur(画像,(n*nの値),標準偏差)
*標準偏差は通常0。n*nの値と標準偏差が大きくなるほどぼかしが強くなる。

#(51*51)のぼかしを行う
new_img=cv2.GaussianBlur(img,(51,51),0)

ノイズ(荒)の除去

cv2.fastNlMeansDenoisingColored(画像)で行う。カラー画像でなければ関数名の「Colored」は抜いて良い。(NlMeansとは、Non-local Means Filterというノイズ除去のフィルターを指す、Denoisingはノイズを除去するという意味)

new_img=cv2.fastNlMeansDenoisingColored(img)

収縮・膨張

・ノイズ処理の別の方法として、画像を一度収縮してから再度膨張させる方法がある。この方法は「閾値処理」のノイズ除去に使われることが多い。
cv2.dilate(画像,フィルタ) で膨張、cv2.erode(画像,フィルタ) で収縮。

#imgを閾値処理
new_img=cv2.threshold(img,127,255,cv2.THRESH_BYNARY)
#フィルタの定義
filter=np.array([[0,1,0],[1,0,1],[0,1,0]],np.uint8)
#収縮して膨張
new_img=cv2.erode(new_img,filter)
new_img=cv2.dilate(new_img,filter)

まとめ

・画像データはOpenCVで処理する。
・OpenCVでは、imread()で画像読み込み、imshow()で画像出力、resize()でリサイズ、flip()で反転、bitwise_notで色反転ができる。その他、画像作成やトリミング、回転などもできる。
・応用として、threshold()で閾値処理、bitwise_and()でマスキング、GaussianBlur()でぼかしを行う。その他、ノイズを除去することもできる。

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

Quip APIを作成してみた

はじめに

Quip上のスプレットシートに保存されたテキストを解析するために、Quip APIでQuipの特定のドキュメントにアクセスした。その際のアクセス方法を個人用にメモしておく。
※個人メモなので省略しまくってます。

Access Tokenの取得

POSTMANでTokenを取得する。
詳しくは、Quip API Documentationを参照

GET https://platform.quip.com/1/users/current

実際にアクセスする

Github上のスプレットシートを取得する関数を用いてフォルダとドキュメントを取得する。

quip_analysis.py
import quip

# access to the quip
client = quip.QuipClient(access_token=<access_token>)

# Get your thread_id from the URL of your document
user = client.get_authenticated_user()
starred = client.get_folder(user["starred_folder_id"])

# get the spreadsheet
spreadsheet = client.get_second_spreadsheet(thread_id=<thread_id>)
parsedSpreadsheet = client.parse_spreadsheet_contents(spreadsheet)

スプレット形式のデータを取得できるのでデータフレームに落とし込む

quip_analysis.py
# create the dataframe
    counter = 0
    spreadsheetData = []
    colNames = []

    for rows in parsedSpreadsheet["rows"]:
        cells = rows["cells"]
        rowData = []
        for key, value in cells.items():
            if counter == 0:
                colNames.append(key)
            rowData.append(value['content'])
        spreadsheetData.append(rowData)
        counter += 1   
    l = pd.DataFrame(spreadsheetData, columns=colNames)

このあと形態素解析でドキュメント上の頻出ワードを抽出した。

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

Djangoで日本語出力がとまどった話

image.png

取りあえず、Djangoを使ってDBから出力をしていたのですが、どうにもうまくいかない感じ。
そこでバグ解決メモ。

原因:Python3はUbuntuの環境変数で文字コードを変える!

Python 3で日本語をprintする際のUnicodeEncodeErrorはLANGに気をつける

こちらの記事によると、Python3はOSの環境変数によって使用する文字コードを変える
わたし、さくらVPSを使っているのですが、さくらのUbuntuはデフォルトで日本語パッケージを入っていないので、
DB側がUTF8、Pythonコンソール上がLatin1になっており、
Pythonコンソールで文字コードエラーが発生していた模様

解決策:Ubuntuの日本語パッケージを導入する

という訳で、Ubuntuのデフォルトロケールを変更します!

'''
% sudo apt-get install language-pack-ja
% sudo update-locale LANG=ja_JP.UTF-8
% sudo reboot
'''

取りあえず、テキストは動作

image.png

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

CentOS でJupyter Notebookをサービス化する

はじめに

Python 環境をCent OS で構築する際の手順をメモする.
あくまでも構築手順に関するメモなので,実際に本番環境で構築する際は,今回の情報で本番環境を構築・運用するわけではない.
環境は以下の通りで,VMware 上の仮想マシンで構築する.

  • OS: Cent OS 7.5.1804
  • 仮想化ソフト: VMware Workstation Pro 15.5.6 build-16341506
  • CPU: 1 Processors
  • メモリ: 1 GB
  • ディスク: 50.0 GB
  • Network Adapter: NAT (192.168.249.129/24)

やりたいことは以下のとおりである.

  • Python 3 のインストール
  • Qiskit のインストール
  • Anaconda のインストール
  • Jupyter のインストール
  • Jupyter のサービス化

以下の手順からは,OS インストール済みで実施することを前提としている.
約束事として,#はroot ユーザー,$ は一般ユーザー,>>> はpython実行中を表すプロンプトとする.
初めて実施するので,遠回りや設定被りは上等です.

まず初めに,アップデートしておく.

OSのアップデート
# yum update 

Python 3 のインストール

標準で使用できるPython 2 ではなく,Python 3 を使用したいので,インストールする.

実行コマンド
# yum update python3

インストール後,インストール確認も含めバージョン情報を表示する.
Python 3.6.8 がインストールされていることが分かる.

Pythonのバージョン確認
# python3 --version
Python 3.6.8

また,python コマンドで今回導入したpython3 を使用したいため,設定を行う.

プロファイル編集
# vim ~/.bashrc

.bashrc の末尾に,以下の内容を追記する.
追記後,一度ログアウトし,再度ログインする.

/root/.bashrc
alias python="python3" 
alias pip="pip3"

試しに,python コマンドでバージョン確認してみる.
python3 が指定されていることが分かる.

Pythonのバージョン確認
# python --version
Python 3.6.8

Anaconda のインストール

pyenv のインストールにgit が必要なため,まずはgit をインストールする.

gitのインストール
# yum install git

pyenv をインストールしていく.

pyenvのインストール
# git clone https://github.com/yyuu/pyenv.git ~/.pyenv
# pyenv install -l | grep anaconda
  anaconda-1.4.0
  anaconda-1.5.0
  anaconda-1.5.1
  anaconda-1.6.0
  anaconda-1.6.1
  anaconda-1.7.0
  anaconda-1.8.0
  anaconda-1.9.0
  anaconda-1.9.1
  anaconda-1.9.2
  anaconda-2.0.0
  anaconda-2.0.1
  anaconda-2.1.0
  anaconda-2.2.0
  anaconda-2.3.0
  anaconda-2.4.0
  anaconda-4.0.0
  anaconda2-2.4.0
  anaconda2-2.4.1
  anaconda2-2.5.0
  anaconda2-4.0.0
  anaconda2-4.1.0
  anaconda2-4.1.1
  anaconda2-4.2.0
  anaconda2-4.3.0
  anaconda2-4.3.1
  anaconda2-4.4.0
  anaconda2-5.0.0
  anaconda2-5.0.1
  anaconda2-5.1.0
  anaconda2-5.2.0
  anaconda2-5.3.0
  anaconda2-5.3.1
  anaconda2-2018.12
  anaconda2-2019.03
  anaconda2-2019.07
  anaconda3-2.0.0
  anaconda3-2.0.1
  anaconda3-2.1.0
  anaconda3-2.2.0
  anaconda3-2.3.0
  anaconda3-2.4.0
  anaconda3-2.4.1
  anaconda3-2.5.0
  anaconda3-4.0.0
  anaconda3-4.1.0
  anaconda3-4.1.1
  anaconda3-4.2.0
  anaconda3-4.3.0
  anaconda3-4.3.1
  anaconda3-4.4.0
  anaconda3-5.0.0
  anaconda3-5.0.1
  anaconda3-5.1.0
  anaconda3-5.2.0
  anaconda3-5.3.0
  anaconda3-5.3.1
  anaconda3-2018.12
  anaconda3-2019.03
  anaconda3-2019.07
  anaconda3-2019.10
  anaconda3-2020.02

最新のanaconda をインストールする.

anacondaのインストール
# pyenv install anaconda3-5.3.1

設定をしていく.

anacondaの設定
# pyenv rehash
# pyenv global anaconda3-5.3.1
# echo 'export PATH="$PYENV_ROOT/versions/anaconda3-5.3.1/bin/:$PATH"' >> ~/.bashrc
# source ~/.bashrc
# conda update conda

バージョンを確認してみる.

condaのバージョン確認
# conda --version
conda 4.8.4

Qiskit のインストール

ここまでで,anaconda のインストールが完了したので,次にQiskit をインストールする.

Qiskitのインストール
# pip install qiskit

インストールされているか,確認する.

Qiskitのインストール確認
# conda list | grep qiskit
qiskit                    0.21.0                   pypi_0    pypi
qiskit-aer                0.6.1                    pypi_0    pypi
qiskit-aqua               0.7.5                    pypi_0    pypi
qiskit-ibmq-provider      0.9.0                    pypi_0    pypi
qiskit-ignis              0.4.0                    pypi_0    pypi
qiskit-terra              0.15.2                   pypi_0    pypi

次に,インストールされているQiskit のバージョンを確認する.

python実行
# python
Qiskitのバージョン確認
>>> import qiskit
>>> qiskit.__qiskit_version__
{'qiskit-terra': '0.15.2', 'qiskit-aer': '0.6.1', 'qiskit-ignis': '0.4.0', 'qiskit-ibmq-provider': '0.9.0', 'qiskit-aqua': '0.7.5', 'qiskit': '0.21.0'}

確認が完了したら,Ctrl-D で,python を終了する.

Jupyter のインストール

次に,jupyter をインストールしてみる.
コマンド補完をしてみると,すでに jupyter がインストールされているようなので,実行してみると以下のエラーが出力された.

jupyterの実行エラー
# jupyter --version
Traceback (most recent call last):
  File "/root/.pyenv/versions/anaconda3-5.3.1/bin/jupyter", line 7, in <module>
    from jupyter_core.command import main
ModuleNotFoundError: No module named 'jupyter_core'

一度conda からjupyter をインストールしてみる.

jupyterのインストール
# conda install jupyter

すると,以下のようなエラーが出力されてインストールが完了しない.

jupyterインストール時のエラー
failed with initial frozen solve. Retrying with flexible solve.
Solving environment: - 強制終了

ググってみて,以下のコマンドを実行してみた.

condaのアップデート
# conda update --all

上記のコマンドでもエラーがでた.次に試しに以下のコマンドを実行した.

OpenCVのインストール
# conda install -c menpo opencv

こっちはうまくいった.再度jupyter をインストールしてみる.

jupyterのインストール再挑戦
# conda install jupyter
Collecting package metadata (repodata.json): done
Solving environment: done

# All requested packages already installed.

ちゃんとインストールされているようにみえる.
バージョン情報を確認してみる.

jupyterのバージョン確認
# jupyter --version
4.4.0

ちゃんとインストールされていて,動作もする.

Jupyter Notebook の設定を行う.

jupyterの起動準備
# jupyter --path
config:
    /root/.jupyter
    /root/.pyenv/versions/anaconda3-5.3.1/etc/jupyter
    /usr/local/etc/jupyter
    /etc/jupyter
data:
    /root/.local/share/jupyter
    /root/.pyenv/versions/anaconda3-5.3.1/share/jupyter
    /usr/local/share/jupyter
    /usr/share/jupyter
runtime:
    /run/user/1000/jupyter
# mkdir ~/.jupyter
# touch ~/.jupyter/jupyter_notebook_config.py
# mkdir ~/jupyter_files
# touch ~/jupyter_files/test.py
# ipython
ログインパスワードの設定
Python 3.7.0 (default, Oct  9 2018, 10:31:47) 
Type 'copyright', 'credits' or 'license' for more information
IPython 6.5.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from notebook.auth import passwd

In [2]: passwd()
Enter password: 
Verify password: 
Out[2]: 'sha1:da72b0981831:41b14f79bbfeda08322cfdb3a056a58fc70c65ea'

In [3]: exit

Out[2] のパスワードのハッシュ値は次のコンフィグファイルの編集で使用するため,テキストエディタ等に控えておく.

コンフィグファイルの編集
vim ~/.jupyter/jupyter_notebook_config.py

コンフィグファイルに以下を追記する.
パスワードのハッシュ値については,ログインパスワードの設定のところで出力された値をそれぞれコピー&ペーストすること.

/root/.jupyter/jupyter_notebook_config.py
c = get_config()

c.NotebookApp.ip = '*'
c.NotebookApp.open_browser = False
c.NotebookApp.port = 8888
c.NotebookApp.password = u'sha1:da72b0981831:41b14f79bbfeda08322cfdb3a056a58fc70c65ea'
c.NotebookApp.notebook_dir = '/root/jupyter_files/'

Jupyter Notebook を起動する.本環境はお試し環境であり,root ユーザーで設定しているので,以下のコマンドを実行する.

notebookの起動
# jupyter notebook --allow-root

その後,Web ブラウザを用いてhttp://localhost:8888/ にアクセスしてみる.

JN.png

アクセスできたので,先ほど設定したログインパスワードでログインしてみる.

JNlogined.png

無事ログインできた.先ほど作成した確認用のファイルtest.py も確認できる.

次に,ホストマシンのWeb ブラウザを用いてアクセスできるようにする.
ファイアウォールでポート8888 を許可する.

ファイアウォールの許可設定
# firewall-cmd --add-port=8888/tcp --zone=public --permanent
success
# firewall-cmd --reload
success

ホストマシンのWeb ブラウザからhttp://192.168.249.129:8888/ にアクセスしてみる.

JN_host.png

無事にアクセスできた.

Jupyter のサービス化

Jupyter をsystemd で制御できるように設定する.まずは,jupyter のパスを確認する.

jupyterのパス確認
# which jupyter
/root/.pyenv/versions/anaconda3-5.3.1/bin/jupyter
定義ファイルの作成
# vim /etc/systemd/system/notebook.service

※以下の内容ではうまく実行できません

/etc/systemd/system/notebook.service
[Unit]
Description = Jupyter Notebook

[Service]
Type=simple
PIDFile=/var/run/jupyter-notebook.pid
ExecStart=/root/.pyenv/versions/anaconda3-5.3.1/bin/jupyter notebook
WorkingDirectory=/root/
User=root
Group=root
Restart=always

[Install]
WantedBy = multi-user.target
設定ファイル再読み込み
# systemctl daemon-reload
サービス起動
# systemctl start notebook

サービスの状態を確認すると,失敗していた.

サービスの状態確認(失敗)
# systemctl status notebook
● notebook.service - Jupyter Notebook
   Loaded: loaded (/etc/systemd/system/notebook.service; disabled; vendor preset: disabled)
   Active: failed (Result: start-limit) since 金 2020-09-18 15:21:44 JST; 1min 8s ago
  Process: 18766 ExecStart=/root/.pyenv/versions/anaconda3-5.3.1/bin/jupyter notebook (code=exited, status=1/FAILURE)
 Main PID: 18766 (code=exited, status=1/FAILURE)

 9月 18 15:21:44 localhost.localdomain systemd[1]: notebook.service: main process exited, code=exited, status=1/FAILURE
 9月 18 15:21:44 localhost.localdomain systemd[1]: Unit notebook.service entered failed state.
 9月 18 15:21:44 localhost.localdomain systemd[1]: notebook.service failed.
 9月 18 15:21:44 localhost.localdomain systemd[1]: notebook.service holdoff time over, scheduling restart.
 9月 18 15:21:44 localhost.localdomain systemd[1]: Stopped Jupyter Notebook.
 9月 18 15:21:44 localhost.localdomain systemd[1]: start request repeated too quickly for notebook.service
 9月 18 15:21:44 localhost.localdomain systemd[1]: Failed to start Jupyter Notebook.
 9月 18 15:21:44 localhost.localdomain systemd[1]: Unit notebook.service entered failed state.
 9月 18 15:21:44 localhost.localdomain systemd[1]: notebook.service failed.

原因をいろいろ考えていると,最初の起動は--allow-root を付与していたのに,今回の設定ファイルでは付与し忘れていたのを思い出した.
以下のように設定ファイルを再編集する.

/etc/systemd/system/notebook.service
[Unit]
Description = Jupyter Notebook

[Service]
Type=simple
PIDFile=/var/run/jupyter-notebook.pid
ExecStart=/root/.pyenv/versions/anaconda3-5.3.1/bin/jupyter notebook --allow-root
WorkingDirectory=/root/
User=root
Group=root
Restart=always

[Install]
WantedBy = multi-user.target

再度,設定ファイルの読み込みをしてサービスを起動させる.

設定ファイル再読み込みとサービス起動
# systemctl daemon-reload
# systemctl start notebook

サービスの状態を確認する.

サービスの状態確認(成功)
# systemctl status notebook
● notebook.service - Jupyter Notebook
   Loaded: loaded (/etc/systemd/system/notebook.service; enabled; vendor preset: disabled)
   Active: active (running) since 金 2020-09-18 15:29:38 JST; 3min 8s ago
 Main PID: 19068 (jupyter-noteboo)
    Tasks: 1
   CGroup: /system.slice/notebook.service
           └─19068 /root/.pyenv/versions/anaconda3-5.3.1/bin/python /root/.pyenv/versions/anaconda3-5.3.1/bin/jupyter-notebook --...

 9月 18 15:29:38 localhost.localdomain systemd[1]: Started Jupyter Notebook.
 9月 18 15:29:39 localhost.localdomain jupyter[19068]: [W 15:29:39.391 NotebookApp] WARNING: The notebook server is listeni...nded.
 9月 18 15:29:39 localhost.localdomain jupyter[19068]: [I 15:29:39.393 NotebookApp] The port 8888 is already in use, trying...port.
 9月 18 15:29:39 localhost.localdomain jupyter[19068]: [I 15:29:39.423 NotebookApp] JupyterLab extension loaded from /root/...erlab
 9月 18 15:29:39 localhost.localdomain jupyter[19068]: [I 15:29:39.423 NotebookApp] JupyterLab application directory is /ro...r/lab
 9月 18 15:29:39 localhost.localdomain jupyter[19068]: [I 15:29:39.426 NotebookApp] Serving notebooks from local directory:...files
 9月 18 15:29:39 localhost.localdomain jupyter[19068]: [I 15:29:39.426 NotebookApp] The Jupyter Notebook is running at:
 9月 18 15:29:39 localhost.localdomain jupyter[19068]: [I 15:29:39.426 NotebookApp] http://(localhost.localdomain or 127.0....8889/
 9月 18 15:29:39 localhost.localdomain jupyter[19068]: [I 15:29:39.427 NotebookApp] Use Control-C to stop this server and s...ion).
Hint: Some lines were ellipsized, use -l to show in full.

今度は無事に立ち上がった見たい.
自動実行を有効にする.

自動実行の有効化と確認
# systemctl enable notebook
# systemctl is-enabled notebook
enabled

自動起動が有効化された.確認のため,ゲストOSを再起動したあと,ホストマシンから先ほどのURL でアクセスしてみたが,無事にアクセスできた.これで常にJupyter Notebook が立ち上がっている状態になり,Web ブラウザを用いて利用可能になった.

おわりに

今回試してみて思ったのが,root ユーザーでの実行は推奨されていないうえ,コマンドもオプションが必要になってきていろいろ面倒なので,サービスユーザーを作成して当ユーザーでサービスを実行したほうが良いと感じた.
あと,別記事で書いていますが,これやるならJupyterHub の方がよいと感じた.

参考文献

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

スタックを Python で書いてみた

スタックって、そもそも何?
はい、データを貯めて、取り出す。それだけです。
但し、取り出し方は最後に貯めたデータを最初に取り出せるようにしたいです。
Σ(oдΟ;)

例えばですが以下にあるようにデータを 6 個、縦に積み上げながら
格納できる箱があったとします。
図1.PNG

まずは、箱を空にして、A を格納した後に、取り出してみましょう。
図2.PNG
A を格納して、取り出したら、元の空の状態に戻りました。
簡単ですね。

ではデータの格納を Push , 取り出しを Pop と命名したうえで、
データの Push / Pop を動きをもう少し図にしてみました。
A , B を Push し、最後に pop してみました。最後に箱に残るのは A ですよね。
図3.PNG
このようにして、最後に Push したデータを最初に Pop するスタックの動きをイメージできるようになりました。

箱の深さが 6 無くても良かったんでは!?
細かい事は気にしない(笑)。
さぁ、次は Python で書いてみましょう。

っとその前に、ここでの記述が分からない場合は、
Progate での学習をお勧めします!!
https://prog-8.com/

では最初にスタックを実現するための箱を用意しましょう。
イメージはこんな感じです。
図4.PNG
str という名の箱を用意し、何個データが入るかを capacity としました。
ptr は 1 ~ 6 の位置を表しています。
先ほどの図に ptr の動きを足してみました。
図5.PNG
こんな感じで、ptr(ポインタ)は Push 出来る箱の位置を表しています。
ptr をイメージできたところで、やっと Python です。
箱を作ってみました。

stack.py
    def __init__(self,capacity:int = 10):
        self.str  = [None] * capacity
        self.capa = capacity
        self.ptr  = 0

" def __ init __ " 用意したプログラム内の初期値を定めます! っという決まり文句です。
プログラムが走るときに最初に読み込んで貰わないと話が始まらないですよね。
次に Push です。

stack.py
    def push(self,value):
        if self.ptr >= self.capa:
            raise top.full
        self.str[self.ptr] = value 
        print(self.str[self.ptr])
        self.ptr += 1

間に print が入っていますが、無視しても良いです。
大切なのは、最初に ptr の位置が Full になっていないか確認することです。
ptr == capacity であれば Full (お腹いっぱい)
ptr > capacity であれば、プログラムが暴走してる!! ってことになります。
if self.ptr >= self.capa: で確認していることが分かります。
Full でなければ self.str[self.ptr] = value とし、無事に Push 完了です。
次の Push に備えて ptr を一つインクリメントしておきます。
Push でやっていることは以上です。

次に Pop です。

stack.py
    def pop(self):
        if self.ptr <= 0:
            raise top.empty
        self.ptr -= 1
        return self.str[self.ptr]

ポインタ ptr が マイナスになっていたとすると、
底を突き抜けて、地面にデータをねじ込むことになるので
一応 if 文でチェックを掛けます。
ここで疑問に思った方も居ると思いますので
補足しますが、Python の世界では格納領域は 1 から始まるわけではなく、
0 から始まります。
最初の図が良くなかった、すいません。
図9.PNG
よって ptr = 0 の場合は、空を意味します。Empty ってやつです。
空じゃなければ以下のように ptr を 1 減らして、
取り出すデータを指定します。
図10.PNG
後は return self.str[self.ptr] とすることで、格納したデータを取り出すことが出来ます。

いやいや、return でデータは返せたけど、
指定した領域は空になってないよね!?
Yes ! その通り!!バコ~ン!( - -)/☆(_)

実はですね、スタックは ptr の位置でデータの管理をしています。
たとえ、空にする処理をしていなくても、次の Push で自動的に書き換えられますよね?
だから ptr の動きだけ見ていればスタックは出来てしまうのです。
っというわけで、取り急ぎ用意したスタックの全体像を載せておきます。

stack.py
class top:
    class full(Exception):
        pass
    class empty(Exception):
        pass    

    def __init__(self,capacity:int = 10):
        self.str  = [None] * capacity
        self.capa = capacity
        self.ptr  = 0

    def push(self,value):
        if self.ptr >= self.capa:
            raise top.full
        self.str[self.ptr] = value 
        print(self.str[self.ptr])
        self.ptr += 1

    def pop(self):
        if self.ptr <= 0:
            raise top.empty
        self.ptr -= 1
        return self.str[self.ptr]

test = top()

while True:
    num = int(input("select 1.push , 2.pop : "))

    if num == 1 :
        s = int(input("enter data: "))
        try:
            test.push(s)
        except test.full:
            print("full!")
    elif num == 2:
        try:
            x = test.pop()
            print(x)
        except test.empty:
            print("empty!")
    else:
        break

多分、"__ init __" , push , pop が肝なので、そこが分かれば、
ご理解いただけると思っています。

初めての投稿ですので、不足分、分かりにくい所があれば
ガンガン優しくご指摘御願い致します!!m(_ _)m

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

Amazon LinuxにLightFMをインストールしようとしてハマった話

タイトルそのままなんですが、備忘録として。

結論から言うと「Amazon LinuxのgccにはOpenMPの実行環境が入っていないので手動再インストールが必要」ということ。

LightFMに関してはこの辺を確認してみてください

さて、ひとまずLightFMのインストールは

pip install lightfm

でOKなのですが、これをAmazon Linux上で普通に実施すると
omp.h: そのようなファイルやディレクトリはありません
というエラーが出てきます。

で、ここの記事を参考にすると以下の通り

原因ですが、yumでインストールされるgcc7.2には、Qulacsが使っているOpenMPのヘッダーファイルomp.hが含まれていない様子。

で、どうするかというと

sudo yum erase gcc72 gcc72-c++ libgcc72

で一旦gcc7.2を削除します。その上で、あらためてgcc 7.4.0を手作業でインストールします。

お~そういうことですか。

というわけで、gccをソースからビルド/インストールしないといけないわけですが、そのまんまな記事が。大変参考になりました。

ほぼこちらに書いてあることそのまんまですが、バージョンに関しては2020/9/23現在は10.2.0が最新ということでそこだけ変えて。実行コマンドは以下の通り。

wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-10.2.0/gcc-10.2.0.tar.gz
tar zxvf gcc-10.2.0.tar.gz 
cd gcc-10.2.0
./contrib/download_prerequisites 
mkdir build
cd build
../configure --enable-languages=c,c++ --prefix=/usr/local --disable-bootstrap --disable-multilib
make > /dev/null
sudo make install all

make実施時に結構時間かかりますので、気長に待ちましょう。

で、上記実施後再度

pip install lightfm

を実行するとすんなりインストール出来ます。

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

KintoneのREST API用スニペット(APIトークン認証用ヘッダーの書き方、ログイン名パスワード用認証ヘッダーの書き方、クエリの書き方)

一通り使ってみたのでよく使うものに関しての備忘録

インターン先でKintoneから一部のデータを利用するためにREST APIを初めてしっかり勉強しました。そのうえでKintone特有の書き方っぽいものだけまとめました。

基本的にKintoneのドキュメントにはJavaScriptのスニペットしか置いていないので、どこまでがREST APIの仕様でどこからがKintone REST APIの仕様なのかわからない部分が多かったのでKintone REST APIの仕様の部分だけをピックアップした感じです。

下記URLの一部をpythonように書き下しました。
https://developer.cybozu.io/hc/ja/articles/201941754

APIトークン利用時のヘッダー

headers = {"X-Cybozu-API-Token": api_token}

IDとパスワードで認証するときのヘッダー

import base64
str_ = str(id_) +':' + str(password)#'id:password'
encoded = base64.b64encode(str_.encode('utf-8'))  
header = {
    "X-Cybozu-Authorization":encoded
}

レコードをとるときにクエリを書いて一括取得するときのURLの書き方
参考URL
https://developer.cybozu.io/hc/ja/articles/202331474

import urllib
url = 'https://{ドメイン名}.cybozu.com/k/v1/records.json?app={}&query={}'.format('アプリ番',urllib.parse.quote('クエリを記入'))
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Machine Learning : Supervised - Linear Discriminant Analysis

目標

フィッシャーの線形判別分析を数式で理解して、scikit-learn で試す。

微分積分、線形代数が既習であることを前提としています。

理論

フィッシャーの線形判別分析は、データを射影した後のカテゴリー間の分布が重ならないような $w$ を求める教師あり手法で、判別という名前が付いていますが実用上は次元削減に用いられます。

フィッシャーの線形判別分析

フィッシャーの線形判別分析の導出

データ $x$ を $w$ で射影すると、射影後のデータ $y$ は、

y = w^T x

となります。このとき、$y$ におけるカテゴリーの分布ができるだけ離れるような $w$ を求めることになります。下図のような場合、最適な $w$ は青の点群と橙の点群を黒の直線上の中抜きの点に射影します。

05_lda_reduction.png

ここで、上図のような 2カテゴリーのデータについて考えます。カテゴリー 1, 2 の平均ベクトルは以下のように表せます。

\mu_1 = \frac{1}{N_1} \sum^{N_1}_{i \in C_1} x_i \\
\mu_2 = \frac{1}{N_2} \sum^{N_2}_{i \in C_2} x_i

平均ベクトルを $w$ で射影したものを $m_1 = w^T\mu_1, m_2 = w^T\mu_2$ で表すと、射影後の平均値の差

m_1 - m_2 = w^T (\mu_1 - \mu_2)

が大きいほどカテゴリー間の分離度が大きくなります。したがって、$w$ を最大すればよいことになります。しかし本当に求めたいのは $w$ の射影方向なので、$|w|^2 = 1$ という制約を付けておきます。しかしこれだけでは上手くいかないので、各カテゴリーの分散も考慮します。各カテゴリーの射影後のクラス内分散 $s^2_1, s^2_2$ は、

s^2_1 = \sum^{N_1}_{i \in C_1} (w^T x_i - w^T \mu_1)^2 \\
s^2_2 = \sum^{N_2}_{i \in C_2} (w^T x_i - w^T \mu_2)^2

射影後の分散は小さいほどよいので、$s^2_1, s^2_2$ を合わせた全クラス内分散 $s^2 = s^2_1 + s^2_2$ を最小化すればよいことになります。

ここで、射影後の平均値の最大化と射影後の分散の最小化の両方を考慮する評価関数として、以下のフィッシャーの基準 $J(w)$ を定義します。

J(w) = \frac{(m_1 - m_2)^2}{s^2_1 + s^2_2}

また、クラス間共分散行列を $S_B = (\mu_1 - \mu_2)(\mu_1 - \mu_2)^T$ とすると、クラス間変動 $(m_1 - m_2)^2$ は、

\begin{align}
(m_1 - m_2)^2 &= \left( w^T(\mu_1 - \mu_2) \right)^2 \\
&= \left( w^T(\mu_1 - \mu_2) \right) \left( w^T(\mu_1 - \mu_2) \right)^T \\
&= w^T (\mu_1 - \mu_2)(\mu_1 - \mu_2)^T w \\
&= w^T S_B w
\end{align}

と表せます。さらにクラス内分散 $s^2_k$ は、

\begin{align}
s^2_k &= \sum_{i \in C_k} (y_i - m_k)^2 \\
&= \sum_{i \in C_k} \left( w^T (x_i - \mu_k) \right)^2 \\
&= \sum_{i \in C_k} \left( w^T(x_i - \mu_k) \right) \left( w^T(x_i - \mu_k) \right)^T \\
&= w^T \sum_{i \in C_k} (x_i - \mu_k)(x_i - \mu_k)^T w \\
&= w^T S_k w
\end{align}

と表せるので、全クラス内分散 $s^2_1 + s^2_2$ は、総クラス内共分散行列を $S_W = S_1 + S_2$ として、

s^2_1 + s^2_2 = w^T (S_1 + S_2) w = w^T S_W w

と表せます。したがって、フィッシャーの基準 $J(w)$ は、

J(w) = \frac{w^T S_B w}{w^T S_W w}

となり、これを最大化することになります。

フィッシャーの線形判別分析の学習

求めるべきは最大値なので、フィッシャーの基準 $J(w)$ を $w$ について微分して 0 として解きます。

\begin{align}
\frac{\partial J(w)}{\partial w} &= \frac{2S_B w \cdot w^TS_Ww - w^TS_Bw \cdot 2S_Ww}{(w^TS_Ww)^2} \\
&= \frac{2}{w^TS_Ww} \left( S_Bw - \frac{w^TS_Bw}{w^TS_Ww} S_Ww \right) = 0
\end{align}

ここで、$\lambda = \frac{w^TS_Bw}{w^TS_Ww}$ とおいて、

\frac{\partial J(w)}{\partial w} = \frac{2}{w^TS_Ww} (S_Bw - \lambda S_Ww) = 0 \\
(S_Bw - \lambda S_Ww) = 0

したがって、下の式の一般化固有値問題を解くことになります。

S_Bw = \lambda S_Ww

ここで、$S_W$ が正則行列であれば、

\lambda w = S^{-1}_WS_Bw

とできて、通常の固有値問題になります。さらに、

S_Bw = (\mu_1 - \mu_2)(\mu_1 - \mu_2)^Tw \propto (\mu_1 - \mu_2)

となることから、

w \propto S^{-1}_WS_Bw \propto S^{-1}_W (\mu_1 - \mu_2)

として、最適な $w$ を求めることができます。

実装

実行環境

ハードウェア

・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 で公開しています。

fisher_lda.py

結果

今回は scikit-learn の提供する iris dataset を用いることにしました。

実行結果は以下のようになります。setosa はよく分離されており、versicolor と virginica は一部被っていますが、まずまず分離できているように見えます。

05_fisher_lda.png

参考

1.2. Linear and Quadratic Discriminant Analysis

平井有三.『はじめてのパターン認識』,森北出版,2012.

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

Python(スライス表記)を読むときの2つのルール

今回はPython のスライス表記について書いていきます。

スライスは単純なルールで表記されますが、初学者に方にとっては
何回か使用していると、ややこしくなってしまう瞬間があるかと思います。

その際、意識しておくべき2つのルールについてお話していきます。

■ スライスを読むときの、2つのルール

①データの順番は0番目から始まる(リストの基本)
②スライスの終わりは $n-1$ となる

実際に例をあげて説明していきます。

下記のようなデータを用意します。
image.png
ルール①では単純に、先頭データから0番目、1番目、2番目・・・と順番が決められているので
それをもとに、数字を抽出していきます。
image.png
上記のように、インデックス番号(0から始まる順位付け)をもとに数字の抽出ができました。
このルールだけならば、まだ簡単だとは思います。

次に、②のルールについて説明していきます。
ここで、ややこしい問題が発生します。
image.png
今回のリストでは、0から数えて5番目のデータが10でしたが
スライスで5番目までを指定して抽出しても、9までしか出力されていません。

このようにスライスでは、インデックス番号の範囲を指定する際に
終わりの番号の一歩手前、 $n-1$ までの順番までしか抽出されません。(今回は $n=5$)
実際に他の数字も入れて確かめてみます。
image.png
初学者の方は、学習中にルール①は意識して写経などをされるかと思いますが
いきなり上記のようなスライスの表記が出てくると、複雑に考えてしまうことがあるかと思います。

その際には、最初にお伝えした2つのルールをもとに考えてみてください。

【スライスを読むときのルール】
①データの順番は0番目から始まる
②スライスの終わりは $n-1$ となる
image.png

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

cronでGithubにPushするプロセスを自動化する

やったこと

Pythonとcrontabを使って、データファイル(pklフォーマット)を一週間に一回(土曜日の午後7時)に更新されるようにしました。

毎週更新されるデータをGithubに自動的にPushしたく、同じくcrontabを使ってやってみた。

環境

  • Linux Mint 19
  • Python 3.6.9
  • git 2.17.1

GithubでSSH接続する

ssh接続する方法はたくさんネットに上がっているので割愛します。
(参考: GitHubにssh接続できるようにする)

GITコマンドでプッシュしてみる

ssh接続が出来たら、gitコマンドでPushしてみる。

アップしたいデータファイルはplayers_data.pklというpythonのpickleというモジュールを使用したフォーマットです。

git add players_data.pkl
git commit -m "最初のコミット"
git remote add origin git@github.com:<ユーザー名>/<レポジトリ名>
git push origin master

自動化のPythonコードを作成

この一連の作業を自動化するPythonのコードを書きます。

使用するのはsubprocessというModuleで、bashコマンドをPythonで実行することが出来ます。(参考:Python の subprocess

new_data.py
import os
import datetime
import subprocess as cmd

#input values
new_date = datetime.datetime.now().strftime('%Y%m%d')
current_dir = os.getcwd()

#Subprocessで自動化
cp = cmd.run(f"git -C {current_dir} add players_score.pkl", check=True, shell=True)
cp = cmd.run(f"git -C {current_dir} commit -m 'scoresheet updated on {new_date}'", check=True, shell=True)
cp = cmd.run(f"git -C {current_dir} push origin master", check=True, shell=True)
print('Githubへのアップ成功!')

この段階でpython3 new_data.py をbashで実行し、ちゃんとGithubにアップされているか確認してもよいでしょう。

SSH のラッパースクリプト(git-ssh.sh)を作成

ここが今回引っかかったところなのですが、crontabでgit push origin masterを実行しようとすると、以下のようなエラーがでます。

Command 'git -C /path/to/folder/ push origin master' returned non-zero exit status 128.

cron から自動で git push するを参考にすると、

cron から自動で git push したい時、ssh の秘密鍵を明示的に指定して git push する必要があります。

というわけで以下のSHファイルを.sshのあるフォルダーに置いてやることが必要です。

/path/to/.ssh/git-ssh.sh
#!/bin/sh
exec ssh -oIdentityFile=/path/to/.ssh/id_rsa "$@"

このSHファイルに実行権限を与えることも必要。

cd /path/to/.ssh
chmod +x git-ssh.sh

SHファイルでPythonを動かす

直接Crontabでやってもいいのですが、今回はSHファイルを作成してPythonを動かすようにします。

scripts.sh
#!/bin/sh
cd /path/to/folder
export GIT_SSH=/path/to/.ssh/git-ssh.sh
python3 new_data.py > output_newdata.txt

crontabでSHファイルを定期的に実行する

最後にcrontabで定期実行の方法です。

まずcrontab -eをbashで実行し、crontabを開けます。

Cronの設定の仕方は割愛しますが、毎週土曜日の午後7:00にscripts.shを定期実行したいときは下記のようにします。(参考:crontabのガイドライン

00 19 * * 6 bash /path/to/folder/scripts.sh

これでうまく定期実行のできるようになりました。

参考サイトまとめ

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

【音響解析】xeno-cantoで鳥の鳴き声データを集める

xeno-cantoとは

鳥の鳴き声データベース。jsonで扱えるAPIがある。
今回はPythonを使って日本におけるFulica属(オオバン属)の鳴き声を一括収集した。

ソースコード

import requests
import json
import urllib

url = "https://www.xeno-canto.org/api/2/recordings?query=gen:Fulica+cnt:japan" 

# json取得(requests.get)

response = requests.get(url)
jsonData = response.json()["recordings"]

# ダウンロード部(ファイル名は{id}.mp3)

for data in jsonData:
    url = ("http:"+data["file"]) 
    title = data["id"]
    urllib.request.urlretrieve(url,"{0}.mp3".format(title))

urlについて

Xeno-cantoのAPIドキュメントによると、クエリには、
* gen : 一般名
* sp : specific name
* ssp : subsupecific name
* en : english name
* cnt : country
などを指定できる。

今回の

url = "https://www.xeno-canto.org/api/2/recordings?query=gen:Fulica+cnt:japan" 

では、gen : Fulica, cut : Japanで指定している。

ダウンロード部について

url = ("http:"+data["file"])

において、["file"]というキーの中に、ダウンロード用urlが入っている。
(http:がついていないので、追記。)

環境

python 3.7.4 (Anaconda)
MacOS Catalina 10.15.5

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

【音声解析】xeno-cantoで鳥の鳴き声データを集める

xeno-cantoとは

鳥の鳴き声データベース。jsonで扱えるAPIがある。
今回はPythonを使って日本におけるFulica属(オオバン属)の鳴き声を一括収集した。

ソースコード

import requests
import json
import urllib

url = "https://www.xeno-canto.org/api/2/recordings?query=gen:Fulica+cnt:japan" 

# json取得(requests.get)

response = requests.get(url)
jsonData = response.json()["recordings"]

# ダウンロード部(ファイル名は{id}.mp3)

for data in jsonData:
    url = ("http:"+data["file"]) 
    title = data["id"]
    urllib.request.urlretrieve(url,"{0}.mp3".format(title))

urlについて

Xeno-cantoのAPIドキュメントによると、クエリには、
* gen : 一般名
* sp : specific name
* ssp : subsupecific name
* en : english name
* cnt : country
などを指定できる。

今回の

url = "https://www.xeno-canto.org/api/2/recordings?query=gen:Fulica+cnt:japan" 

では、gen : Fulica, cut : Japanで指定している。

ダウンロード部について

url = ("http:"+data["file"])

において、["file"]というキーの中に、ダウンロード用urlが入っている。
(http:がついていないので、追記。)

環境

python 3.7.4 (Anaconda)
MacOS Catalina 10.15.5

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