Kinpatu Diary

プログラミング×マラソン

BeautifulsoupでWebスクレイピングをしよう!【Python】

f:id:kinpatucom821:20180902230641p:plain

・Webスクレイピングとは

スクレイピングとは、Webサイトからデータをえて、任意の情報を抽出できるので大変便利です。

Pythonスクレイピングするのに欠かせないライブラリが【BeautifulSoup】。

このBeautifulSoupを使うと手軽にHTMLやXMLから情報を抽出でき解析をする事ができるのですが、あくまでも解析するライブラリなのでダウンロードをする際はurllibを使うことになる。



・Beautifulsoupのインストール

pipでインストール
コマンドラインまたはターミナルから

pip3 install beautifulsoup

・BeautifulSoupの基本的な使い方

HTMLを文字列で指定し、その文字列を解析し任意の部分を抽出して表示する。


beautifulsoup_base.py

#ライブラリを取り込む
from bs4 import BeautifulSoup

#解析したいHTMLを指定

html='''
<html><body>
    <h1>スクレイピングとは?</h1>
    <p>Webページを解析する事。</p>
    <p>任意の箇所を抽出する事</p>
    </body></html>
'''

#HTMLを解析する

#Beautifulsoupの第一引数にHTMLを指定、第二引数に解析を行うパーサーの種類を指定

#HTMLを解析するには、'html.parser'という値を指定する

soup=BeautifulSoup(html,'html.parser')

#任意の部分を抽出する

#HTMLの構造と同じように(.)で繋げて書く

#「soup.html.body.h1」と書くことで、<html><body><h1>の値にアクセスする事ができる

#最初のpも同じだが、次のpは「p1.next_sibling.next_sibling」と書く。

#1回目のnext_siblingでは</p>の直後の改行やスペースが得られ、2回目のnext_siblingで2つ目のpタグにアクセスする。

h1=soup.html.body.h1
p1=soup.html.body.p
p2=p1.next_sibling.next_sibling

#要素のテキストを抽出する

print('h1='+h1.string)
print('p1='+p1.string)
print('p2='+p2.string)


コマンドライン(実行)

$ python beautifulsoup_base.py
h1=スクレイピングとは?
p1=Webページを回移籍する事。
p2=任意の箇所を抽出する事

パーセプトロンモデルのトレーニング【scikit-learn】

scikit-learnを使いもっとゴリゴリいろいろなデータを分類してみたい!!!と思っているけど知識があまりにもザックリしているので、しっかりとした理論を学び実践する事で少しずつでもパワーアップしていきたい。

まず最初はscikit-learnライブラリを使い始めるにあたり、パーセプトロンモデルをIrisのデータセットを使いトレーニングしていく。


gistacf422c4fc4c9aedb9b6836b012a8e57

次回はクラスが完全に線形分離不可能な場合であっても コストの最小値に収束する線形分類器の学習をしよう!!

kerasでmnistデータの学習を試してみる【Kaggle:Digit Recognizer】

最初のKaggleチャレンジはAidemyでも教材になっていたmnistです。

『Digit Recognizer』
Learn computer vision fundamentals with the famous MNIST data

上位は1.00ばかりというもう数字は全部認識できてるよ!!状態なのですが僕もここにチャレンジしてみます!!

Kaggleを覗いてみると上位の人のコードを勉強することができるし、上位の人同士がお互いのコードを指摘しあいやり取りを覗くことの出来るとても素敵な場所でした。

それに加え書かれているのが全て英語なので機械学習の英語の勉強にもなるという一石二鳥の効果もあります。

ちなみに僕は英語がほとんど出来ないのですが毎日単語を調べたりしながら読むことで少しづつですが読めるようになってきました。

Kaggleって最高ですね♪


事前準備としてAidemyの『教師あり分類』、『ディープラーニング基礎』、『CNNを用いた画像認識』を一通り学習しました。

ではチャレンジ行ってみましょう!!

#必要なライブラリをimport
import numpy as np
import pandas as pd

#可視化ライブラリ
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

#訓練データとテストデータに切り分ける為
from sklearn.model_selection import train_test_split

from keras.models import Sequential,load_model
from keras.layers import Dense,Activation,Dropout,Conv2D,MaxPool2D,Flatten,BatchNormalization
from keras.utils.np_utils import to_categorical
#データの読み込み
train=pd.read_csv('train.csv')
test=pd.read_csv('test.csv')

train.columns

f:id:kinpatucom821:20180829182852p:plain

train_Xにlabelを削除したdata、train_yにlabelという形に分ける。

train_ytrain_y=train['label']
train_X=train.drop(['label'],axis=1)


#train_yに0~9までの数字が何個ずつ入っているか調べる
g=sns.countplot(train_y)
train_y.value_counts()


f:id:kinpatucom821:20180829183500p:plain

手書き文字のため1が一番描きやすかったのか一番多いですね。

#正規化
train_X=train_X/255.0
test=test/255.0

画像サイズが28×28でmnistはグレースケールの1チャンネルなのでデータを再編成する。

train_X=train_X.values.reshape(-1,28,28,1)
test=test.values.reshape(-1,28,28,1)

ラベルが0から9までの10桁の数字なのでone-hotベクトルにエンコード
例 0→[00000001]

One-hot - Wikipedia

train_y=to_categorical(train_y,num_classes=10)

訓練データ(9割)とテストデータ(1割)に分割しvalidationセットも作る

train_X,val_X,train_y,val_y=train_test_split(train_X,train_y,test_size=0.1,random_state=42)

モデル作り
Aidemyで学んだ過学習防止の為のDropoutとバッチ正規化のBachNormalizationも組み込む。

model=Sequential()
model.add(Conv2D(input_shape=(28,28,1),filters=32,activation='relu',kernel_size=(3,3),strides=(1,1),padding='same'))
model.add(Conv2D(filters=32,kernel_size=(3,3),activation='relu',strides=(1,1),padding='same'))
model.add(MaxPool2D(pool_size=(2,2)))
model.add(Dropout(rate=0.25))
model.add(Conv2D(filters=64,kernel_size=(3,3),padding='same',activation='relu'))
model.add(Conv2D(filters=64,kernel_size=(3,3),padding='same',activation='relu'))
model.add(MaxPool2D(pool_size=(2,2),strides=(2,2)))
model.add(Dropout(rate=0.25))
model.add(Flatten())
model.add(BatchNormalization())
model.add(Dense(128,activation='relu'))
model.add(BatchNormalization())
model.add(Dense(10,activation='softmax'))

model.compile(loss='categorical_crossentropy',optimizer='adadelta',metrics=['accuracy'])

これだけで実行すると正解率が全然上がらなかったのでImageDataGeneratorを使いデータの水増しを行う。
その際、vertical_flipやhorizonal_flipは6と9とか2と7とか反転させると間違えて分類する可能性があるため指定しない。

from keras.preprocessing.image import ImageDataGenerator
datagen=ImageDataGenerator(featurewise_center=False, 
                          samplewise_center=False, 
                          featurewise_std_normalization=False, 
                          samplewise_std_normalization=False, 
                          rotation_range=0.1, 
                          width_shift_range=0.1, 
                          height_shift_range=0.1, 
                          zoom_range=0.1, 
                          horizontal_flip=False, 
                          vertical_flip=False, )

datagen.fit(train_X)

準備が出来たので学習する。

history=model.fit_generator(imggen.flow(train_X,train_y,batch_size=32),epochs=15,
                           validation_data=(val_X,val_y),
                           verbose=1,steps_per_epoch=len(train_X)/32)

f:id:kinpatucom821:20180830203903p:plain

学習が終わったので可視化する。

fig, ax = plt.subplots(2,1)
ax[0].plot(history.history['loss'], color='b', label="Training loss")
ax[0].plot(history.history['val_loss'], color='r', label="validation loss",axes =ax[0])
legend = ax[0].legend(loc='best', shadow=True)

ax[1].plot(history.history['acc'], color='b', label="Training accuracy")
ax[1].plot(history.history['val_acc'], color='r',label="Validation accuracy")
legend = ax[1].legend(loc='best', shadow=True)

f:id:kinpatucom821:20180830203923p:plain


いい感じに学習できている気がする🤔

学習結果をみて見る。

score=model.evaluate(val_X,val_y,verbose=1)
print('loss:{0[0]}\n acc :{0[1]}'.format(score))

#可視化
for i in range(10):
    plt.subplot(2,5,i+1)
    plt.imshow(val_X[i].reshape((28,28)),'gray')
    
plt.suptitle('First ten numbers',fontsize=20)
plt.show()

pred=np.argmax(model.predict(val_X[:10]),axis=1)
print(pred)

model.summary()

f:id:kinpatucom821:20180830204034p:plain

f:id:kinpatucom821:20180830204048p:plain


学習結果は表示出来たけど結局何を間違えていたのか???

とても気になったので間違えていた数字を確認してみる。

何が正解で何が不正解だったのかという分類精度を知るには混同行列 (Confusion matrix)を使えばいいみたいなので、Confision matrixを使ってみようと思います。

sklearnのドキュメントを読む。
http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html#sphx-glr-auto-examples-model-selection-plot-confusion-matrix-py

ドキュメントから必要な物を抜き出して使う(最後の5行以外は全てコピペ)

from sklearn.metrics import confusion_matrix
import itertools

def plot_confusion_matrix(cm, classes,
                          normalize=False,
                          title='Confusion matrix',
                          cmap=plt.cm.Blues):
    """
    This function prints and plots the confusion matrix.
    Normalization can be applied by setting `normalize=True`.
    """
    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
        print("Normalized confusion matrix")
    else:
        print('Confusion matrix, without normalization')

    print(cm)

    plt.imshow(cm, interpolation='nearest', cmap=cmap)
    plt.title(title)
    plt.colorbar()
    tick_marks = np.arange(len(classes))
    plt.xticks(tick_marks, classes, rotation=45)
    plt.yticks(tick_marks, classes)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.tight_layout()
    plt.ylabel('True label')
    plt.xlabel('Predicted label')


pred_y = model.predict(val_X)
pred_y_classes = np.argmax(pred_y,axis = 1) 
tue_y= np.argmax(val_y,axis = 1) 
confusion_mtx = confusion_matrix(tue_y, pred_y_classes) 
plot_confusion_matrix(confusion_mtx, classes = range(10)) 

f:id:kinpatucom821:20180830204129p:plain

4と9の判別が難しかったみたいですね。

他も5,6,8の数字の誤分類が多かったです。

もっといろんな方法を調べて正解率を上げるチャレンジをしよう!!

Seabornで美しいグラフ表示-FaceGridのTutorialをやってみよう!

kaggleでコードを学習しているとグラフの表示でSeabornを使っている事が多いので、せっかくなのでしっかりSeabornを学習してみようと思う。

Seaborn
Seabornはmatplotlibに基づくPythonのデータ視覚化ライブラリです。 魅力的で有益な統計的なグラフィックスを描くための高水準のインターフェースを提供します。
https://seaborn.pydata.org/


matplotlibの機能をより美しく、より簡単にグラフを描く事ができるラッパー的な存在のようですね。


では、Seabornをインストールします。

pip install seaborn


今回はkaggleで頻出していたSeabornのFaceGridをチュートリアルから学習していきます。

チュートリアルを見ると、numpy、pandas、matplotlib、scipyをimportする必要があるのでまだ入れてなかったらインストール。

pip install numpy
pip install pandas
pip install matplotlib
pip install scipy


FaceGridの引数

class = seaborn.FacetGrid(data,row = None,col = None,hue =None,col_wrap = None,sharex = true,sharey = True,height = 3,aspect = 1,palette =None,row_order =None,col_order =None,hue_order =None,hue_kws =None,dropna = True,legend_out = True,despine = True,margin_titles = False,xlim =None,ylim =None,subplot_kws =None,gridspec_kws =None,size =None)

data:DataFrame
各列が変数であり各行が観測値である、きちんとしたデータフレーム

row,col,hue
データのサブセットを定義する変数。グリッド内に描画される。

・メソッド

_init __(data [、row、col、hue、col_wrap、...])
matplotlib FigureとFacetGridオブジェクトを初期化。

add_legend([legend_data、title、label_order])
凡例を描画します。凡例を軸の外に置き、図のサイズを変更。

map(func、* args、** kwargs)
各ファセットのデータのサブセットにプロット関数を適用。

map_dataframe(func、* args、** kwargs)
.mapと似ているが、argsを文字列として渡し、kwargsにデータを挿入。

set(** kwargs)
各サブプロットAxesの属性を設定。

set_axis_labels([x_var、y_var])
グリッドの左列と下列に軸ラベルを設定。

set_titles([template、row_template、...])
各ファセットまたはグリッドマージンの上にタイトルを描画。

set_xlabels([label])
グリッドの一番下の行にx軸にラベルを付ける。

set_xticklabels([labels、step])
グリッドの一番下の行にx軸目盛のラベルを設定。

set_ylabels([label])
グリッドの左欄のy軸にラベルを付ける。

set_yticklabels([labels])
グリッドの左欄にy軸目盛のラベルを設定。

お会計(total_bill)と喫煙者と非喫煙者のランチ、ディナータイムのデータからグラフを描写

単変数のプロットを描く

import seaborn as sns;sns.set(style='ticks',color_codes=True)
import matplotlib.pyplot as plt
tips=sns.load_dataset('tips')
g=sns.FacetGrid(tips,col='time',row='smoker')
g=g.map(plt.hist,'total_bill')

f:id:kinpatucom821:20180819162615p:plain

追加のキーワード引数をマップ関数に渡し描く

import numpy as np
bins=np.arange(0,65,5)
g=sns.FacetGrid(tips,col='time',row='smoker')
g=g.map(plt.hist,'total_bill',bins=bins,color='r')

f:id:kinpatucom821:20180819162742p:plain

g=sns.FacetGrid(tips,col='time',row='smoker')
g=g.map(plt.scatter,'total_bill','tip',edgecolor='w')

f:id:kinpatucom821:20180819162851p:plain

変数の1つのプロット要素に色を割り当てる

g = sns.FacetGrid(tips, col="time",  hue="smoker")
g = (g.map(plt.scatter, "total_bill", "tip", edgecolor="w") .add_legend())

f:id:kinpatucom821:20180819163115p:plain

g = sns.FacetGrid(tips, col="day", height=4, aspect=.5)
g = g.map(plt.hist, "total_bill", bins=bins)

f:id:kinpatucom821:20180819163354p:plain

g = sns.FacetGrid(tips, col="smoker", col_order=["Yes", "No"]) 
g = g.map(plt.hist, "total_bill", bins=bins, color="m")

f:id:kinpatucom821:20180819163515p:plain


別のカラーパレットを使用

kws = dict(s=50, linewidth=.5, edgecolor="w")
 g = sns.FacetGrid(tips, col="sex", hue="time", palette="Set1",hue_order=["Dinner", "Lunch"])
g = (g.map(plt.scatter, "total_bill", "tip", **kws) .add_legend())

f:id:kinpatucom821:20180819163739p:plain

カラーのマッピングに辞書を使用する

pal = dict(Lunch="seagreen", Dinner="gray")
g = sns.FacetGrid(tips, col="sex", hue="time", palette=pal,hue_order=["Dinner", "Lunch"])
g = (g.map(plt.scatter, "total_bill", "tip", **kws).add_legend())

f:id:kinpatucom821:20180819165243p:plain

色ごとにマーカーを変えて見る

g = sns.FacetGrid(tips, col="sex", hue="time", palette=pal,hue_order=["Dinner", "Lunch"],hue_kws=dict(marker=["^", "v"]))
g = (g.map(plt.scatter, "total_bill", "tip", **kws).add_legend())

f:id:kinpatucom821:20180819165449p:plain

Seabornはmatplotlibと比べて簡単にグラフを描く事ができるのでとても便利ですね😄