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

【Python】macOSでpipを使えるようにする

ラズパイだとapt-getでpipがインストールできますが、macOSでpipをインストールしたく、そこで詰まってしまったのでその備忘録です。
Homebrewで一発とか思ってたのですが、brew install pipではインストールできません。1

公式ページのインストール方法に従う

公式のやり方に従って、以下のcurlコマンドで get-pipというPythonファイルがダウンロードします。

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py

ダウンロードしたget-pip.pyを実行するとpipがインストールされます。

$ python3 get-pip.py

インストール完了

whichコマンドを叩いてパスが表示されるので、インストール自体は完了です。

$ which pip
/usr/local/bin/pip

環境

  • macOS 10.15.5 Catalina
  • pip 20.2.1
  • Python 3.8.5

参考サイト


  1. ※筆者はHomeBrewが大好きです。 

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

PythonでEDINET・TDNETから有価証券報告書・四半期報告書・決算短信のXBRLをダウンロードする

タイトルがクソ長い..
金融界隈で定量的な分析やデータサイエンスをやっている9uantです.
twitterもやってるので,興味ある方はぜひフォローしていただけると!

タイトルの通り,決算書類のXBRLを手早くダウンロードするためのコードを共有する.
解説も追々書いていきたい.
以下の2ステップをとる.

  • XBRLへのリンクをDataFrame化する
  • DataFrameからXBRLのzipファイルをダウンロードする
import os
import glob
import shutil
import re
import time
from datetime import date, timedelta, datetime
from dateutil.relativedelta import relativedelta

import requests
from bs4 import BeautifulSoup
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.by import By
import zipfile

import numpy as np
import pandas as pd
import json

XBRLへのリンクをDataFrame化する

EDINET

EDINETにはAPIが存在するため容易.

def edinet_xbrl_link(annual=True, quarter=True, codes=None, year=0,month=0,day=0):
    '''
    特定の企業の,もしくは全ての有価証券報告書・四半期報告書のXBRLのリンクのDataFrameを作成する

    Parameters:
        annual: bool, default True
            True の場合に有価証券報告書を取得する
        quarter: bool, default Ture
            True の場合に四半期報告書を取得する
        codes: None, int, float, str, or list (codes[code]=int, float,or str), default None
            None の場合に全ての企業のデータを取得する
            銘柄コードを指定すると,それらの企業のデータのみを取得する:point_up_tone4:
        year, month, day: int, default 0
            現在から何日前までのデータを取得するかを指定する(最大5年)

    Returns:
        database: pandas.DataFrame
            database['code']: str
                5桁の証券コード
            database['type']: str
                'annual' or 'quarter'
            database['date']: datetime.date
                公開日
            database['title']: str
                表題
            database['URL']: str
                XBRLのzipファイルをダウンロードするURL
    '''
    edinet_url = "https://disclosure.edinet-fsa.go.jp/api/v1/documents.json"

    # codesを文字型の配列に統一する.
    if codes != None:
        if type(codes) in (str, int, float):
            codes = [int(codes)]
        for code in codes:
            # 4桁の証券コードを5桁に変換
            if len(str(int(code)))==4:
                code = str(int(code))+'0'

    # datetime型でfor文を回す
    def date_range(start, stop, step = timedelta(1)):
        current = start
        while current < stop:
            yield current
            current += step

    # 結果を格納するDataFrameを用意
    database = pd.DataFrame(index=[], columns=['code','type','date','title','URL'])

    for d in date_range(date.today()-relativedelta(years=year, months=month, days=day)+relativedelta(days=1), date.today()+relativedelta(days=1)):
        # EDINET API にアクセス
        d_str = d.strftime('%Y-%m-%d')
        params = {'date' : d_str, 'type' : 2}
        res = requests.get(edinet_url, params=params, verify=False)
        json_res = json.loads(res.text)
        time.sleep(5)

        # 正常にアクセスできない場合
        if json_res['metadata']['status']!='200':
            print(d_str, 'not accessible')
            continue

        print(d_str, json_res['metadata']['resultset']['count'])# 日付と件数を表示

        # 0件の場合
        if len(json_res['results'])==0:
            continue

        df = pd.DataFrame(json_res['results'])[['docID', 'secCode', 'ordinanceCode', 'formCode','docDescription']]
        df.dropna(subset=['docID'], inplace=True)
        df.dropna(subset=['secCode'], inplace=True)
        df.rename(columns={'secCode': 'code', 'docDescription': 'title'}, inplace=True)
        df['date'] = d
        df['URL'] = df['docID']
        df['URL'] = "https://disclosure.edinet-fsa.go.jp/api/v1/documents/" + df['URL']

        # 指定された証券コードのみを抽出
        if codes != None:
            df = df[df['code'] in codes]

        if annual == True:
            df1 = df[(df['ordinanceCode']=='010') & (df['formCode']=='030000')]
            df1['type'] = 'annual'
            database = pd.concat([database, df1[['code', 'type', 'date','title', 'URL']]], axis=0, join='outer').reset_index(drop=True)

        if quarter == True:
            df2 = df[(df['ordinanceCode']=='010') & (df['formCode']=='043000')]
            df2['type'] = 'quarter'
            database = pd.concat([database, df2[['code', 'type', 'date','title', 'URL']]], axis=0, join='outer').reset_index(drop=True)

    return database

TDNET

TDNETからのデータの収集にはseleniumを用いる.
フリーワード検索結果が200件までしか表示されないため,証券コードから検索する関数と,日付から検索する関数を別々に作成した.

def tdnet_xbrl_link_by_code(codes):
    '''
    指定された企業の決算短信をXBRLへのリンクのDataFrameを作成する

    Parameters:
        codes: None, int, float, str, or list (codes[code]=int, float,or str), default None
            None の場合に全ての企業のデータを取得する

    Returns:
        database: pandas.DataFrame
            database['code']: str
                5桁の証券コード
            database['type']: str
                'annual' or 'quarter'
            database['date']: datetime.date
                公開日
            database['title']: str
                表題
            database['URL']: str
                XBRLのzipファイルをダウンロードするURL
    '''

    # codesを文字型の配列に統一する.
    if type(codes) in (str, int, float):
        codes = [int(codes)]
    for i, code in enumerate(codes):
        # 4桁の証券コードを5桁に変換
        if len(str(int(code)))==4:
            codes[i] = str(int(code))+'0'

    database = pd.DataFrame(index=[], columns=['code','type','date','title','URL'])

    for code in codes:
        # ブラウザを起動する
        chromeOptions = webdriver.ChromeOptions()
        chromeOptions.add_argument('--headless') # ブラウザ非表示
        driver = webdriver.Chrome(options=chromeOptions)
        driver.get("https://www.release.tdnet.info/onsf/TDJFSearch/I_head")

        # 検索ワードを送る
        duration = driver.find_element_by_name('t0')
        select = Select(duration)
        select.options[-1].click()
        inputElement = driver.find_element_by_id("freewordtxt")
        inputElement.send_keys(code)
        inputElement.send_keys(Keys.RETURN)
        time.sleep(5)

        # 検索結果が表示されたフレームに移動
        iframe = driver.find_element_by_name("mainlist")
        driver.switch_to.frame(iframe)

        # 検索結果が0件の場合に処理を終える
        if driver.find_element_by_id("contentwrapper").text == '該当する適時開示情報が見つかりませんでした。':
            return database

        # 検索結果の表の各行からデータを読み取る
        table = driver.find_element_by_id("maintable")
        trs = table.find_elements(By.TAG_NAME, "tr")
        for i in range(len(trs)):
            title = trs[i].find_elements(By.TAG_NAME, "td")[3].text

            # 訂正書類でなく,XBRLが存在する,指定された企業の決算短信を選択
            if ('決算短信' in title) and ('訂正' not in title) and (len(trs[i].find_elements(By.TAG_NAME, "td")[4].text)!=0) and (code==trs[i].find_elements(By.TAG_NAME, "td")[1].text):
                date = trs[i].find_elements(By.TAG_NAME, "td")[0].text[:10]
                date = datetime.strptime(date, '%Y/%m/%d').date()
                url = trs[i].find_elements(By.TAG_NAME, "td")[4].find_element_by_tag_name("a").get_attribute("href")
                database = database.append(pd.Series([code,'brief',date,title,url], index=database.columns), ignore_index=True)

        driver.quit()

        return database
def tdnet_xbrl_link_by_date(date=None):
    '''
    指定された日付,もしくは全ての決算短信をXBRLへのリンクのDataFrameを作成する

    Parameters:
        date: None or str ('yyyy/mm/dd'), default None
            None の場合に全ての日付のデータを取得する

    Returns:
        database: pandas.DataFrame
            database['code']: str
                5桁の証券コード
            database['type']: str
                'annual' or 'quarter'
            database['date']: datetime.date
                公開日
            database['title']: str
                表題
            database['URL']: str
                XBRLのzipファイルをダウンロードするURL
    '''
    database = pd.DataFrame(index=[], columns=['code','type','date','title','URL'])

    # ブラウザを起動する
    chromeOptions = webdriver.ChromeOptions()
    chromeOptions.add_argument('--headless') # ブラウザ非表示
    driver = webdriver.Chrome(options=chromeOptions)
    driver.get("https://www.release.tdnet.info/inbs/I_main_00.html")

    duration = driver.find_element_by_name('daylist')
    select = Select(duration)
    for i in range(1, len(select.options)):
        driver.get("https://www.release.tdnet.info/inbs/I_main_00.html")
        duration = driver.find_element_by_name('daylist')
        select = Select(duration)
        d = datetime.strptime(select.options[i].text[:10], '%Y/%m/%d').date()
        print(select.options[i].text)

        if (date == None) or (date == select.options[i].text[:10]):
            select.options[i].click()
            time.sleep(5)

            # 検索結果が表示されたフレームに移動
            iframe = driver.find_element_by_id("main_list")
            driver.switch_to.frame(iframe)

            # 検索結果が0件の場合に処理を終える
            if driver.find_element_by_id("kaiji-text-1").text!='に開示された情報':
                continue

            # 最後のページまで処理を続ける
            while True:
                # 検索結果の表の各行からデータを読み取る
                table = driver.find_element_by_id("main-list-table")
                trs = table.find_elements(By.TAG_NAME, "tr")
                for i in range(len(trs)):
                    title = trs[i].find_elements(By.TAG_NAME, "td")[3].text

                    # 訂正書類でなく,XBRLが存在する,指定された企業の決算短信を選択
                    if ('決算短信' in title) and ('訂正' not in title) and (len(trs[i].find_elements(By.TAG_NAME, "td")[4].text)!=0):
                        code = trs[i].find_elements(By.TAG_NAME, "td")[1].text
                        url = trs[i].find_elements(By.TAG_NAME, "td")[4].find_element_by_tag_name("a").get_attribute("href")
                        database = database.append(pd.Series([code, 'brief', d, title,url], index=database.columns), ignore_index=True)

                if len(driver.find_element_by_class_name("pager-R").text)!=0:
                    driver.find_element_by_class_name("pager-R").click()
                    time.sleep(5)
                else:
                    # 「次へ」の文字が存在しない場合に処理を終了する
                    break
    driver.quit()
    return database

DataFrameからXBRLをダウンロードする

def dl_xbrl_zip(codes=None, database):
    '''
    XBRLへのリンクをリスト化したDataFrameを参照して,XBRLのzipファイルをダウンロードする

    Parameters:
        codes: None, int, float, str, or list (codes[code]=int, float,or str), default None
            None の場合に全ての企業のXBRLを取得する

        database: pandas.DataFrame
            database['code']: str
                5桁の証券コード
            database['type']: str
                'annual' or 'quarter'
            database['date']: datetime.date
                公開日
            database['title']: str
                表題
            database['URL']: str
                XBRLのzipファイルをダウンロードするURL

    Returns:
        None
    '''
    database.dropna(subset=['code'], inplace=True)
    database = database.reset_index(drop=True)

    # codesを文字型の配列に統一する
    if codes == None:
        codes = [None]
    else:
        if type(codes) in (str, int, float):
            codes = [int(codes)]
        for i, code in enumerate(codes):
            # 4桁の証券コードを5桁に変換
            if len(str(int(code)))==4:
                codes[i] = str(int(code))+'0'

    for code in codes:
        if code == None:
            df_company = database
        else:
            df_company = database[database['code']==code]
            df_company = df_company.reset_index(drop=True)

        # 証券コードをディレクトリ名とする
        dir_path = database.loc[i,'code']
        if os.path.exists(dir_path)==False:
            os.mkdir(dir_path)

        # 抽出したリストの各行からXBRLへのリンクからzipファイルをダウンロード
        for i in range(df_company.shape[0]):
            # EDINETへアクセスする場合
            if (df_company.loc[i,'type'] == 'annual') or (df_company.loc[i,'type'] == 'quarter'):
                params = {"type": 1}
                res = requests.get(df_company.loc[i,'URL'], params=params, stream=True)

                if df_company.loc[i,'type'] == 'annual':
                    # 有価証券報告書のファイル名は"yyyy_0.zip"
                    filename = dir_path + r'/' + df_company.loc[i,'date'][:4] + r"_0.zip"
                elif df_company.loc[i,'type'] == 'quarter':
                    if re.search('期第', df_company.loc[i,'title']) == None:
                        # 第何期か不明の四半期報告書のファイル名は"yyyy_unknown_docID.zip"
                        filename = dir_path + r'/' + df_company.loc[i,'date'][:4] + r'_unknown_' + df_company.loc[i,'URL'][-8:] + r'.zip'
                    else:
                        # 四半期報告書のファイル名は"yyyy_quarter.zip"
                        filename = dir_path + r'/' + df_company.loc[i,'date'][:4] + r'_' + df_company.loc[i,'title'][re.search('期第', df_company.loc[i,'title']).end()] + r'.zip'

            # TDNETへアクセスする場合
            elif df_company.loc[i,'type'] == 'brief':
                res = requests.get(df_company.loc[i,'URL'], stream=True)

                # 空白文字を埋める
                s_list = df_company.loc[i,'title'].split()
                s = ''
                for i in s_list:
                    s += i

                filename = df_company.loc[i,'date'][:4] + r'_' + s[re.search('期第', s).end()] + r'_brief.zip'

            # 同名のzipファイルが存在する場合,上書きはしない
            if os.path.exists(filename):
                print(df_company.loc[i,'code'],df_company.loc[i,'date'],'already exists')
                continue

            # 正常にアクセスできた場合のみzipファイルをダウンロード
            if res.status_code == 200:
                with open(filename, 'wb') as file:
                    for chunk in res.iter_content(chunk_size=1024):
                        file.write(chunk)
                    print(df_company.loc[i,'code'],df_company.loc[i,'date'],'saved')

    print('done!')
    return None
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Computer Vision : Semantic Segmentation Part1 - ImageNet pretraining VoVNet

目標

Microsoft Cognitive Toolkit (CNTK) を用いたセマンティックセグメンテーションについてまとめました。

Part1 では、セマンティックセグメンテーションのためのバックボーンに使用する CNN の事前学習を行います。
CNN の事前学習には 1,000カテゴリーの ImageNet の画像を使用します。

以下の順で紹介します。

  1. ImageNet からのダウンロードと準備
  2. VoVNet : One-Shot Aggregation module
  3. 訓練における諸設定

導入

ImageNet からのダウンロードと準備

ImageNet [1] は 1.4億枚以上の画像が登録されている大規模な画像データベースです。2017年までは画像認識のコンペティション ILSVCR に使われていました。

今回は ImageNet が管理している画像の URL を使ってダウンロードする方法で 1,000カテゴリーの訓練データを収集しました。ただし、850番目の teddy, teddy bear は一枚もダウンロードできなかったため、Computer Vision : Image Classification Part1 - Understanding COCO dataset で準備した画像で代用しました。

また、ダウンロードした画像には壊れた JPEG ファイルやカテゴリーとは関係のない画像も相当数含まれていたので、自動と手動でクリーニングしました。最終的に収集できた画像は 775,983枚になりました。

今回のディレクトリの構成は以下のようにしました。

COCO
MNIST
NICS
RTSS
 |―ImageNet
  |―n01440764
   |―n01440764_0.jpg
   |―…
 rtss_imagenet.py
 rtss_vovnet57.py
SSMD

VoVNet : One-Shot Aggregation module

畳み込みニューラルネットワークのモデルとして、今回は VoVNet [2] (Variety of View Network) を採用しました。VoVNet は DenseNet [3] よりもメモリ使用量と計算コストを削減した CNN モデルです。

One-Shot Aggregation module

VoVNet では、下図の四角で囲まれた One-Shot Aggregation (OSA) モジュールを使用します。

osa.png

VoVNet57

VoVNet57 のネットワークの構成は以下のようになっています。

Layer Filters Size/Stride Input Output
Convolution2D 64 3x3/2 3x224x224 64x112x112
Convolution2D 64 3x3/1 64x112x112 64x112x112
Convolution2D 128 3x3/1 64x112x112 128x112x112
MaxPooling2D 3x3/2 128x112x112 128x56x56
OSA module 128, 256 3x3/1, 1x1/1 128x56x56 256x56x56
MaxPooling2D 3x3/2 256x56x56 256x28x28
OSA module 160, 512 3x3/1, 1x1/1 256x28x28 512x28x28
MaxPooling2D 3x3/2 512x28x28 512x14x14
OSA module 192, 768 3x3/1, 1x1/1 512x14x14 768x14x14
OSA module 192, 768 3x3/1, 1x1/1 768x14x14 768x14x14
OSA module 192, 768 3x3/1, 1x1/1 768x14x14 768x14x14
OSA module 192, 768 3x3/1, 1x1/1 768x14x14 768x14x14
MaxPooling2D 3x3/2 768x14x14 768x7x7
OSA module 224, 1024 3x3/1, 1x1/1 768x7x7 1024x7x7
OSA module 224, 1024 3x3/1, 1x1/1 1024x7x7 1024x7x7
OSA module 224, 1024 3x3/1, 1x1/1 1024x7x7 1024x7x7
GlobalAveragePooling global 1024x7x7 1024x1x1
Dense 1000 1024x1x1 1000x1x1
Softmax 1000 1000 1000

合計 57層の畳み込みと 32倍のダウンサンプリングで構成されます。パラメータの総数は 31,429,159 です。

畳み込み層ではバイアスを使用せずに、Batch Normalization [4] を適用してから活性化関数へ入力します。

最後の全結合層では、Batch Normalization は使用せずにバイアス項を使用します。

活性化関数 Mish

活性化関数には Mish [5] を採用しました。Mish は ReLU を上回る Swish [6] よりも高い性能が報告されている活性化関数です。Mish は以下の式で表されるように、ソフトプラス関数と tanh関数を組み合わせることで簡単に実装できます。

Mish(x) = x \cdot \tanh \left( \log (1 + e^x) \right)

Mish は下図のようになります。

mish.png

Mish は ReLU の deadly neuron を回避し、ReLU が微分すると不連続なのに対して Mish は何度微分しても連続なので、損失関数が滑らかになって最適化しやすくなります。

訓練における諸設定

入力画像は輝度値の最大値 255 で除算します。

各層のパラメータの初期値は He の正規分布 [7] に設定しました。

損失関数は Cross Entropy Error、最適化アルゴリズムは Stochastic Gradient Decent (SGD) with Momentum を採用しました。モーメンタムは 0.9 に固定しました。

学習率には、Cyclical Learning Rate (CLR) [8] を採用し、最大学習率は 0.1、ベース学習率は 1e-4、ステップサイズはエポック数の 10倍、方策は triangular2 に設定しました。

過学習対策として、L2 正則化の値を 0.0005 に設定しました。

モデルの訓練はミニバッチサイズ 64 のミニバッチ学習によって 100 Epoch を実行しました。

実装

実行環境

ハードウェア

・CPU Intel(R) Core(TM) i7-5820K 3.30GHz
・GPU NVIDIA Quadro RTX 5000 16GB

ソフトウェア

・Windows 10 Pro 1909
・CUDA 10.0
・cuDNN 7.6
・Python 3.6.6
・cntk-gpu 2.7
・cntkx 0.1.50
・numpy 1.17.3
・opencv-contrib-python 4.1.1.26
・pandas 0.25.0
・requests 2.22.0

実行するプログラム

ImageNet からダウンロードするプログラムと訓練用のプログラムは GitHub で公開しています。

rtss_imagenet.py
rtss_vovnet57.py

結果

訓練時の損失関数と誤認識率のログを可視化したものが下図です。左のグラフが損失関数、右のグラフが誤認識率になっており、横軸はエポック数、縦軸はそれぞれ損失関数の値と誤認識率を表しています。

vovnet57_logging.png

これでバックボーンとなる CNN 事前学習モデルができたので、Part2 ではセマンティックセグメンテーションを実現するための機構を加えて完成させます。

参考

ImageNet
Microsoft COCO Common Objects in Context

Computer Vision : Image Classification Part1 - Understanding COCO dataset

  1. Jia Deng, Wei Dong, Richard Socher, Li-Jia Li, Kai Li, and Li Fei-Fei. "ImageNet: A Large-Scale Hierarchical Image Database", IEEE conference on Computer Vision and Pattern Recognition (CVPR). 2009, p. 248-255.
  2. Youngwan Lee, Joong-won Hwang, Sangrok Lee, Yuseok Bae, and Jongyoul Park. "An Energy and GPU-Computation Efficient Backbone Network for Real-Time Object Detection", the IEEE Conference on Computer Vision and Pattern Recognition Workshops. 2019, p. 0-0.
  3. Gao Huang, Zhuang Liu, Laurens van der Maaten, and Kilian Q. Weinberger. "Densely Connected Convolutional Networks", the IEEE conference on Computer Vision and Pattern Recognition (CVPR). 2017. p. 4700-4708.
  4. Ioffe Sergey and Christian Szegedy. "Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift", arXiv preprint arXiv:1502.03167 (2015).
  5. Misra, Diganta. "Mish: A self regularized non-monotonic neural activation function." arXiv preprint arXiv:1908.08681 (2019).
  6. Ramachandran, Prajit, Barret Zoph, and Quoc V. Le. "Searching for activation functions." arXiv preprint arXiv:1710.05941 (2017).
  7. Kaiming He, Xiangyu Zhang, Shaoqing Ren, and Jian Sun. "Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification", The IEEE International Conference on Computer Vision (ICCV). 2015, p. 1026-1034.
  8. Leslie N. Smith. "Cyclical Learning Rates for Training Neural Networks", 2017 IEEE Winter Conference on Applications of Computer Vision. 2017, p. 464-472.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

3.PythonによるAIプログライミング

はじめに

いきなりこのページに来られた方は、親ページから参照をお願いいたします。

ここの目的

pycharmを使用して、python言語でAIをコーディングします。コードはコピペでOKです。
ソースコード内の意味が分からなくても実行できます。

開発環境の立ち上げ

  • Pythonプログラムを作成するためにpycharm(PyCharm Community Edition)を起動します。前回作ったPJが表示されるので「mnist」をクリック
    Pycharm019.png

  • pycharm起動画面 (mnistプロジェクトが起動されました)
    Pycharm021.png

  • このサンプルのソースコードは、AIとは全く関係がないものなので全部消去します (サンプルのソースコードは、CTL+AしてDELキーで簡単に消せます)
    Pycharm022.png

PythonによるAIプログラミング

  • ソースコードは以下です。
main.py
# ------------------------------------------------------------------------------------------------------------
# CNN(Convolutional Neural Network)でMNISTを試す
# ------------------------------------------------------------------------------------------------------------
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from keras.datasets import mnist
from keras import backend as ke
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D


# ------------------------------------------------------------------------------------------------------------
# ハイパーパラメータ
# ------------------------------------------------------------------------------------------------------------
# ハイパーパラメータ ⇒ バッチサイズ、エポック数
# 例えば、訓練データが60,000個で、batch_sizeを6,000とした場合、
# 学習データをすべて使うのに60,000個÷6,000=10回のパラメータ更新が行われる。
# これを1epochと言う。epochが10であれば、10×10=100回のパラメータ更新が行われることとなる。
# epoch数は損失関数(コスト関数)の値がほぼ収束するまでに設定する。
batch_size = 6000           # バッチサイズ
epochs = 5                  # エポック数


# ------------------------------------------------------------------------------------------------------------
# 正誤表関数
# ------------------------------------------------------------------------------------------------------------
def show_prediction():
    n_show = 100                                 # 全部は表示すると大変なので一部を表示
    y = model.predict(X_test)
    plt.figure(2, figsize=(10, 10))
    plt.gray()
    for i in range(n_show):
        plt.subplot(10, 10, (i+1))               # subplot(行数, 列数, プロット番号)
        x = X_test[i, :]
        x = x.reshape(28, 28)
        plt.pcolor(1 - x)
        wk = y[i, :]
        prediction = np.argmax(wk)
        plt.text(22, 25.5, "%d" % prediction, fontsize=12)
        if prediction != np.argmax(y_test[i, :]):
            plt.plot([0, 27], [1, 1], color='red', linewidth=10)
        plt.xlim(0, 27)
        plt.ylim(27, 0)
        plt.xticks([], "")
        plt.yticks([], "")


# ------------------------------------------------------------------------------------------------------------
# keras backendの表示
# ------------------------------------------------------------------------------------------------------------
# print(ke.backend())
# print(ke.floatx())


# ------------------------------------------------------------------------------------------------------------
# MNISTデータの取得
# ------------------------------------------------------------------------------------------------------------
# 初回はダウンロードが発生するため時間がかかる
# 60,000枚の28x28ドットで表現される10個の数字の白黒画像と10,000枚のテスト用画像データセット
# ダウンロード場所:'~/.keras/datasets/'
# ※MNISTのデータダウンロードがNGとなる場合は、PROXYの設定を見直してください
#
# MNISTデータ
#  ├ 教師データ (60,000個)
#  │  ├ 画像データ
#  │  └ ラベルデータ
#  │
#  └ 検証データ (10,000個)
#     ├ 画像データ
#     └ ラベルデータ

# ↓教師データ          ↓検証データ
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# ↑画像    ↑ラベル     ↑画像    ↑ラベル


# ------------------------------------------------------------------------------------------------------------
# 画像データ(教師データ、検証データ)のリシェイプ
# ------------------------------------------------------------------------------------------------------------
img_rows, img_cols = 28, 28
if ke.image_data_format() == 'channels_last':
    X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
    X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)
else:
    X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
    X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)

# 配列の整形と、色の範囲を0~255 → 0~1に変換
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255


# ------------------------------------------------------------------------------------------------------------
# ラベルデータ(教師データ、検証データ)のベクトル化
# ------------------------------------------------------------------------------------------------------------
y_train = np_utils.to_categorical(y_train)      # 教師ラベルのベクトル化
y_test = np_utils.to_categorical(y_test)        # 検証ラベルのベクトル化


# ------------------------------------------------------------------------------------------------------------
# ネットワークの定義 (keras)
# ------------------------------------------------------------------------------------------------------------
print("")
print("●ネットワーク定義")
model = Sequential()

# 入力層 28×28×3
model.add(Conv2D(16, kernel_size=(3, 3), activation='relu', input_shape=input_shape, padding='same'))     # 01層:畳込み層16枚
model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))                                          # 02層:畳込み層32枚
model.add(MaxPooling2D(pool_size=(2, 2)))                                                                 # 03層:プーリング層
model.add(Dropout(0.25))                                                                                  # 04層:ドロップアウト
model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))                                          # 05層:畳込み層64枚
model.add(MaxPooling2D(pool_size=(2, 2)))                                                                 # 06層:プーリング層
model.add(Flatten())                                                                                      # 08層:次元変換
model.add(Dense(128, activation='relu'))                                                                  # 09層:全結合出力128
model.add(Dense(10, activation='softmax'))                                                                # 10層:全結合出力10

# model表示
model.summary()

# コンパイル
# 損失関数 :categorical_crossentropy (クロスエントロピー)
# 最適化   :Adam
model.compile(loss='categorical_crossentropy',
              optimizer='Adam',
              metrics=['accuracy'])

print("")
print("●学習スタート")
f_verbose = 1  # 0:表示なし、1:詳細表示、2:表示
hist = model.fit(X_train, y_train,
                 batch_size=batch_size,
                 epochs=epochs,
                 validation_data=(X_test, y_test),
                 verbose=f_verbose)


# ------------------------------------------------------------------------------------------------------------
# 損失値グラフ化
# ------------------------------------------------------------------------------------------------------------
# Accuracy (正解率)
plt.plot(range(epochs), hist.history['accuracy'], marker='.')
plt.plot(range(epochs), hist.history['val_accuracy'], marker='.')
plt.title('Accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='lower right')
plt.show()

# loss (損失関数)
plt.plot(range(epochs), hist.history['loss'], marker='.')
plt.plot(range(epochs), hist.history['val_loss'], marker='.')
plt.title('loss Function')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper right')
plt.show()


# ------------------------------------------------------------------------------------------------------------
# テストデータ検証
# ------------------------------------------------------------------------------------------------------------
print("")
print("●検証結果")
t_verbose = 1  # 0:表示なし、1:詳細表示、2:表示
score = model.evaluate(X_test, y_test, verbose=t_verbose)

print("")
print("batch_size = ", batch_size)
print("epochs = ", epochs)

print('Test loss:', score[0])
print('Test accuracy:', score[1])


print("")
print("●混同行列(コンフュージョンマトリックス) 横:識別結果、縦:正解データ")
predict_classes = model.predict_classes(X_test[1:60000, ], batch_size=batch_size)
true_classes = np.argmax(y_test[1:60000], 1)
print(confusion_matrix(true_classes, predict_classes))


# ------------------------------------------------------------------------------------------------------------
# 正誤表表示
# ------------------------------------------------------------------------------------------------------------
show_prediction()
plt.show()

  • このソースコードを先程のフィールド(赤枠内)にコピペしてください。
    prog001.png

  • 下記の様に赤線枠内に!赤丸の箇所がある場合は、ライブラリが足りずエラーとなっています。
    prog009.png

prog001+.png

  • 足りていないライブラリは以下で確認できます。
    ライブラリが無い箇所に赤色で下線が付いています。
    prog002.png

  • 足りないライブラリを以下にまとめます
    ライブラリはパッケージに入っています。
    sklearnライブラリだけ、scikit-learnというパッケージに入っているので注意。

No. 足りないライブラリ名 必要なパッケージ名称
1 keras keras
2 numpy numpy
3 matplotlib matplotlib
4 sklearn scikit-learn

パッケージのインストール

●ライブラリを追加したいので、それに適合したパッケージを追加します

  1. anacondaを起動し、[Envionments] -> [python37]をクリック

●kerasパッケージをインストールします

  1. [Installed]を[All]に変更
  2. 検索BOXに[keras]と入力し、kerasパッケージを検索
  3. [keras]のチェックボックスをONに設定
  4. 右下の[Apply]をクリックして適応させる prog003.png
  • Install PackagesのメッセージBOXが出たら、[Apply]をクリックして、kerasパッケージをインストール prog004.png

●numpyパッケージをインストールします

  1. 検索BOXに[numpy]と入力し、numpyパッケージを検索
  2. numpyパッケージがInstallされていることを確認できました。

kerasはnumpyを使用するため、依存関係からkerasパッケージのインストール時に、numpyも自動的にインストールされていました。ということで、numpyのインストール作業は割愛できました。
prog005.png

●matplotlibパッケージをインストールします

prog006.png

  1. 検索BOXに[matplotlib]と入力し、matplotlibパッケージを検索
  2. [matplotlib]のチェックボックスをONに設定
  3. 右下の[Apply]をクリックして適応させる
  4. Install PackagesのメッセージBOXが出たら、[Apply]をクリックして、matplotlibパッケージをインストール

●scikit-learnパッケージをインストールします

  1. 検索BOXに[scikit-learn]と入力し、scikit-learnパッケージを検索
  2. [scikit-learn]のチェックボックスをONに設定
  3. 右下の[Apply]をクリックして適応させる
  4. Install PackagesのメッセージBOXが出たら、[Apply]をクリックして、scikit-learnパッケージをインストール

※sklearnライブラリは、scikit-learnパッケージに入っています。
prog007.png

プログラム実行

  • エラーが全てなくなっていることを確認します。
  1. [Problems]をクリック
  2. Problemsに!赤丸が出ていないことを確認

prog009.png
prog010.png

  • 右上の「」をクリックしてプログラムを実行してください。 prog011.png

出力結果

  • きちんとできると、以下のような結果が得られます。
  • 今回は「Test accuracy: 0.9359999895095825」と結果が得られたので、認識率は93.4%でした。
C:\Users\xxxx\anaconda3\envs\python37\python.exe C:/Users/xxxx/PycharmProjects/mnist_sample/qiita.py
Using TensorFlow backend.

●ネットワーク定義
2020-08-06 11:36:11.346263: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 28, 28, 16)        160       
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 28, 28, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 14, 32)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 14, 14, 32)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 14, 14, 32)        9248      
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 7, 7, 32)          0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 1568)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 128)               200832    
_________________________________________________________________
dense_2 (Dense)              (None, 10)                1290      
=================================================================
Total params: 216,170
Trainable params: 216,170
Non-trainable params: 0
_________________________________________________________________

●学習スタート
Train on 60000 samples, validate on 10000 samples
Epoch 1/5
2020-08-06 11:36:12.480915: W tensorflow/core/framework/cpu_allocator_impl.cc:81] Allocation of 602112000 exceeds 10% of system memory.
2020-08-06 11:36:14.075159: W tensorflow/core/framework/cpu_allocator_impl.cc:81] Allocation of 602112000 exceeds 10% of system memory.

 6000/60000 [==>...........................] - ETA: 36s - loss: 2.3063 - accuracy: 0.0653
12000/60000 [=====>........................] - ETA: 32s - loss: 2.2858 - accuracy: 0.1563
18000/60000 [========>.....................] - ETA: 29s - loss: 2.2630 - accuracy: 0.2346
24000/60000 [===========>..................] - ETA: 24s - loss: 2.2374 - accuracy: 0.2971
30000/60000 [==============>...............] - ETA: 20s - loss: 2.2083 - accuracy: 0.3415
36000/60000 [=================>............] - ETA: 16s - loss: 2.1742 - accuracy: 0.3779
42000/60000 [====================>.........] - ETA: 12s - loss: 2.1342 - accuracy: 0.4095
48000/60000 [=======================>......] - ETA: 8s - loss: 2.0883 - accuracy: 0.4363 
54000/60000 [==========================>...] - ETA: 4s - loss: 2.0373 - accuracy: 0.4610
60000/60000 [==============================] - 44s 733us/step - loss: 1.9787 - accuracy: 0.4864 - val_loss: 1.3384 - val_accuracy: 0.7674
Epoch 2/5

 6000/60000 [==>...........................] - ETA: 39s - loss: 1.3002 - accuracy: 0.7305
12000/60000 [=====>........................] - ETA: 37s - loss: 1.2238 - accuracy: 0.7381
18000/60000 [========>.....................] - ETA: 33s - loss: 1.1505 - accuracy: 0.7432
24000/60000 [===========>..................] - ETA: 27s - loss: 1.0788 - accuracy: 0.7513
30000/60000 [==============>...............] - ETA: 23s - loss: 1.0145 - accuracy: 0.7597
36000/60000 [=================>............] - ETA: 18s - loss: 0.9617 - accuracy: 0.7652
42000/60000 [====================>.........] - ETA: 14s - loss: 0.9165 - accuracy: 0.7698
48000/60000 [=======================>......] - ETA: 9s - loss: 0.8742 - accuracy: 0.7754 
54000/60000 [==========================>...] - ETA: 4s - loss: 0.8390 - accuracy: 0.7804
60000/60000 [==============================] - 50s 831us/step - loss: 0.8084 - accuracy: 0.7856 - val_loss: 0.4861 - val_accuracy: 0.8541
Epoch 3/5

 6000/60000 [==>...........................] - ETA: 41s - loss: 0.4924 - accuracy: 0.8445
12000/60000 [=====>........................] - ETA: 36s - loss: 0.4970 - accuracy: 0.8453
18000/60000 [========>.....................] - ETA: 32s - loss: 0.5020 - accuracy: 0.8486
24000/60000 [===========>..................] - ETA: 28s - loss: 0.5005 - accuracy: 0.8508
30000/60000 [==============>...............] - ETA: 23s - loss: 0.4866 - accuracy: 0.8547
36000/60000 [=================>............] - ETA: 19s - loss: 0.4774 - accuracy: 0.8578
42000/60000 [====================>.........] - ETA: 14s - loss: 0.4730 - accuracy: 0.8603
48000/60000 [=======================>......] - ETA: 9s - loss: 0.4721 - accuracy: 0.8622 
54000/60000 [==========================>...] - ETA: 4s - loss: 0.4641 - accuracy: 0.8648
60000/60000 [==============================] - 52s 862us/step - loss: 0.4574 - accuracy: 0.8666 - val_loss: 0.3624 - val_accuracy: 0.9004
Epoch 4/5

 6000/60000 [==>...........................] - ETA: 44s - loss: 0.3941 - accuracy: 0.8850
12000/60000 [=====>........................] - ETA: 40s - loss: 0.3863 - accuracy: 0.8882
18000/60000 [========>.....................] - ETA: 34s - loss: 0.3731 - accuracy: 0.8912
24000/60000 [===========>..................] - ETA: 29s - loss: 0.3659 - accuracy: 0.8943
30000/60000 [==============>...............] - ETA: 25s - loss: 0.3545 - accuracy: 0.8971
36000/60000 [=================>............] - ETA: 20s - loss: 0.3461 - accuracy: 0.8987
42000/60000 [====================>.........] - ETA: 15s - loss: 0.3417 - accuracy: 0.9001
48000/60000 [=======================>......] - ETA: 10s - loss: 0.3421 - accuracy: 0.9008
54000/60000 [==========================>...] - ETA: 5s - loss: 0.3367 - accuracy: 0.9023 
60000/60000 [==============================] - 52s 874us/step - loss: 0.3332 - accuracy: 0.9033 - val_loss: 0.2740 - val_accuracy: 0.9225
Epoch 5/5

 6000/60000 [==>...........................] - ETA: 44s - loss: 0.2830 - accuracy: 0.9168
12000/60000 [=====>........................] - ETA: 39s - loss: 0.2939 - accuracy: 0.9151
18000/60000 [========>.....................] - ETA: 35s - loss: 0.2872 - accuracy: 0.9168
24000/60000 [===========>..................] - ETA: 30s - loss: 0.2782 - accuracy: 0.9193
30000/60000 [==============>...............] - ETA: 25s - loss: 0.2782 - accuracy: 0.9188
36000/60000 [=================>............] - ETA: 20s - loss: 0.2733 - accuracy: 0.9200
42000/60000 [====================>.........] - ETA: 15s - loss: 0.2686 - accuracy: 0.9217
48000/60000 [=======================>......] - ETA: 10s - loss: 0.2684 - accuracy: 0.9222
54000/60000 [==========================>...] - ETA: 4s - loss: 0.2654 - accuracy: 0.9233 
60000/60000 [==============================] - 52s 872us/step - loss: 0.2634 - accuracy: 0.9236 - val_loss: 0.2180 - val_accuracy: 0.9360

●検証結果

   32/10000 [..............................] - ETA: 5s
  320/10000 [..............................] - ETA: 2s
  608/10000 [>.............................] - ETA: 2s
  928/10000 [=>............................] - ETA: 1s
 1248/10000 [==>...........................] - ETA: 1s
 1568/10000 [===>..........................] - ETA: 1s
 1920/10000 [====>.........................] - ETA: 1s
 2272/10000 [=====>........................] - ETA: 1s
 2624/10000 [======>.......................] - ETA: 1s
 2976/10000 [=======>......................] - ETA: 1s
 3328/10000 [========>.....................] - ETA: 1s
 3680/10000 [==========>...................] - ETA: 1s
 4032/10000 [===========>..................] - ETA: 1s
 4384/10000 [============>.................] - ETA: 1s
 4736/10000 [=============>................] - ETA: 0s
 5088/10000 [==============>...............] - ETA: 0s
 5408/10000 [===============>..............] - ETA: 0s
 5728/10000 [================>.............] - ETA: 0s
 6048/10000 [=================>............] - ETA: 0s
 6368/10000 [==================>...........] - ETA: 0s
 6560/10000 [==================>...........] - ETA: 0s
 6816/10000 [===================>..........] - ETA: 0s
 7104/10000 [====================>.........] - ETA: 0s
 7392/10000 [=====================>........] - ETA: 0s
 7680/10000 [======================>.......] - ETA: 0s
 8000/10000 [=======================>......] - ETA: 0s
 8320/10000 [=======================>......] - ETA: 0s
 8640/10000 [========================>.....] - ETA: 0s
 8960/10000 [=========================>....] - ETA: 0s
 9280/10000 [==========================>...] - ETA: 0s
 9600/10000 [===========================>..] - ETA: 0s
 9920/10000 [============================>.] - ETA: 0s
10000/10000 [==============================] - 2s 196us/step

batch_size =  6000
epochs =  5
Test loss: 0.21799209741055967
Test accuracy: 0.9359999895095825

●混同行列(コンフュージョンマトリックス) 横:識別結果、縦:正解データ
[[ 966    0    1    1    0    1    6    1    4    0]
 [   0 1108    4    2    0    0    3    1   17    0]
 [  12    2  954   18    7    0    7    8   21    3]
 [   2    2    7  938    0   24    0   11   19    7]
 [   1    2    4    1  908    0   10    3    5   48]
 [   5    1    3   18    0  834    9    2   14    6]
 [  18    4    2    2    6   14  906    2    4    0]
 [   1    5   26    7    7    1    0  916    4   60]
 [  10    0    5   23    9   18    8    4  878   19]
 [  10    5    3   13    8    6    0    7    6  951]]

Process finished with exit code 0

正解率

  • 学習回数を増やす度に、正解率がどんどん上昇しているのが分かると思います。 exe001.PNG

損失関数結果

  • 学習回数を増やす度に、損失率がどんどん下降しているのが分かると思います。 exe002.PNG

正誤表

  • 検証データは10,000枚ありますが、全部出力させると大変なので、最初の100枚まで結果を出力させています。
  • 赤色線で示したものが、AIが認識誤りを起こしたものです。
  • 枠内右下にAIが認識した数字を記載しています。 exe003.PNG

以上

  • お疲れ様でした!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

3.PythonによるAIプログラミング

はじめに

いきなりこのページに来られた方は、親ページから参照をお願いいたします。

ここの目的

pycharmを使用して、python言語でAIをコーディングします。コードはコピペでOKです。
ソースコード内の意味が分からなくても実行できます。

開発環境の立ち上げ

  • Pythonプログラムを作成するためにpycharm(PyCharm Community Edition)を起動します。前回作ったPJが表示されるので「mnist」をクリック
    Pycharm019.png

  • pycharm起動画面 (mnistプロジェクトが起動されました)
    Pycharm021.png

  • このサンプルのソースコードは、AIとは全く関係がないものなので全部消去します (サンプルのソースコードは、CTL+AしてDELキーで簡単に消せます)
    Pycharm022.png

PythonによるAIプログラミング

  • ソースコードは以下です。
main.py
# ------------------------------------------------------------------------------------------------------------
# CNN(Convolutional Neural Network)でMNISTを試す
# ------------------------------------------------------------------------------------------------------------
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
from keras.datasets import mnist
from keras import backend as ke
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D


# ------------------------------------------------------------------------------------------------------------
# ハイパーパラメータ
# ------------------------------------------------------------------------------------------------------------
# ハイパーパラメータ ⇒ バッチサイズ、エポック数
# 例えば、訓練データが60,000個で、batch_sizeを6,000とした場合、
# 学習データをすべて使うのに60,000個÷6,000=10回のパラメータ更新が行われる。
# これを1epochと言う。epochが10であれば、10×10=100回のパラメータ更新が行われることとなる。
# epoch数は損失関数(コスト関数)の値がほぼ収束するまでに設定する。
batch_size = 6000           # バッチサイズ
epochs = 5                  # エポック数


# ------------------------------------------------------------------------------------------------------------
# 正誤表関数
# ------------------------------------------------------------------------------------------------------------
def show_prediction():
    n_show = 100                                 # 全部は表示すると大変なので一部を表示
    y = model.predict(X_test)
    plt.figure(2, figsize=(10, 10))
    plt.gray()
    for i in range(n_show):
        plt.subplot(10, 10, (i+1))               # subplot(行数, 列数, プロット番号)
        x = X_test[i, :]
        x = x.reshape(28, 28)
        plt.pcolor(1 - x)
        wk = y[i, :]
        prediction = np.argmax(wk)
        plt.text(22, 25.5, "%d" % prediction, fontsize=12)
        if prediction != np.argmax(y_test[i, :]):
            plt.plot([0, 27], [1, 1], color='red', linewidth=10)
        plt.xlim(0, 27)
        plt.ylim(27, 0)
        plt.xticks([], "")
        plt.yticks([], "")


# ------------------------------------------------------------------------------------------------------------
# keras backendの表示
# ------------------------------------------------------------------------------------------------------------
# print(ke.backend())
# print(ke.floatx())


# ------------------------------------------------------------------------------------------------------------
# MNISTデータの取得
# ------------------------------------------------------------------------------------------------------------
# 初回はダウンロードが発生するため時間がかかる
# 60,000枚の28x28ドットで表現される10個の数字の白黒画像と10,000枚のテスト用画像データセット
# ダウンロード場所:'~/.keras/datasets/'
# ※MNISTのデータダウンロードがNGとなる場合は、PROXYの設定を見直してください
#
# MNISTデータ
#  ├ 教師データ (60,000個)
#  │  ├ 画像データ
#  │  └ ラベルデータ
#  │
#  └ 検証データ (10,000個)
#     ├ 画像データ
#     └ ラベルデータ

# ↓教師データ          ↓検証データ
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# ↑画像    ↑ラベル     ↑画像    ↑ラベル


# ------------------------------------------------------------------------------------------------------------
# 画像データ(教師データ、検証データ)のリシェイプ
# ------------------------------------------------------------------------------------------------------------
img_rows, img_cols = 28, 28
if ke.image_data_format() == 'channels_last':
    X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
    X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)
else:
    X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
    X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)

# 配列の整形と、色の範囲を0~255 → 0~1に変換
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255


# ------------------------------------------------------------------------------------------------------------
# ラベルデータ(教師データ、検証データ)のベクトル化
# ------------------------------------------------------------------------------------------------------------
y_train = np_utils.to_categorical(y_train)      # 教師ラベルのベクトル化
y_test = np_utils.to_categorical(y_test)        # 検証ラベルのベクトル化


# ------------------------------------------------------------------------------------------------------------
# ネットワークの定義 (keras)
# ------------------------------------------------------------------------------------------------------------
print("")
print("●ネットワーク定義")
model = Sequential()

# 入力層 28×28×3
model.add(Conv2D(16, kernel_size=(3, 3), activation='relu', input_shape=input_shape, padding='same'))     # 01層:畳込み層16枚
model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))                                          # 02層:畳込み層32枚
model.add(MaxPooling2D(pool_size=(2, 2)))                                                                 # 03層:プーリング層
model.add(Dropout(0.25))                                                                                  # 04層:ドロップアウト
model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))                                          # 05層:畳込み層64枚
model.add(MaxPooling2D(pool_size=(2, 2)))                                                                 # 06層:プーリング層
model.add(Flatten())                                                                                      # 08層:次元変換
model.add(Dense(128, activation='relu'))                                                                  # 09層:全結合出力128
model.add(Dense(10, activation='softmax'))                                                                # 10層:全結合出力10

# model表示
model.summary()

# コンパイル
# 損失関数 :categorical_crossentropy (クロスエントロピー)
# 最適化   :Adam
model.compile(loss='categorical_crossentropy',
              optimizer='Adam',
              metrics=['accuracy'])

print("")
print("●学習スタート")
f_verbose = 1  # 0:表示なし、1:詳細表示、2:表示
hist = model.fit(X_train, y_train,
                 batch_size=batch_size,
                 epochs=epochs,
                 validation_data=(X_test, y_test),
                 verbose=f_verbose)


# ------------------------------------------------------------------------------------------------------------
# 損失値グラフ化
# ------------------------------------------------------------------------------------------------------------
# Accuracy (正解率)
plt.plot(range(epochs), hist.history['accuracy'], marker='.')
plt.plot(range(epochs), hist.history['val_accuracy'], marker='.')
plt.title('Accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='lower right')
plt.show()

# loss (損失関数)
plt.plot(range(epochs), hist.history['loss'], marker='.')
plt.plot(range(epochs), hist.history['val_loss'], marker='.')
plt.title('loss Function')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper right')
plt.show()


# ------------------------------------------------------------------------------------------------------------
# テストデータ検証
# ------------------------------------------------------------------------------------------------------------
print("")
print("●検証結果")
t_verbose = 1  # 0:表示なし、1:詳細表示、2:表示
score = model.evaluate(X_test, y_test, verbose=t_verbose)

print("")
print("batch_size = ", batch_size)
print("epochs = ", epochs)

print('Test loss:', score[0])
print('Test accuracy:', score[1])


print("")
print("●混同行列(コンフュージョンマトリックス) 横:識別結果、縦:正解データ")
predict_classes = model.predict_classes(X_test[1:60000, ], batch_size=batch_size)
true_classes = np.argmax(y_test[1:60000], 1)
print(confusion_matrix(true_classes, predict_classes))


# ------------------------------------------------------------------------------------------------------------
# 正誤表表示
# ------------------------------------------------------------------------------------------------------------
show_prediction()
plt.show()

  • このソースコードを先程のフィールド(赤枠内)にコピペしてください。
    prog001.png

  • 下記の様に赤線枠内に!赤丸の箇所がある場合は、ライブラリが足りずエラーとなっています。
    prog009.png

prog001+.png

  • 足りていないライブラリは以下で確認できます。
    ライブラリが無い箇所に赤色で下線が付いています。
    prog002.png

  • 足りないライブラリを以下にまとめます
    ライブラリはパッケージに入っています。
    sklearnライブラリだけ、scikit-learnというパッケージに入っているので注意。

No. 足りないライブラリ名 必要なパッケージ名称
1 keras keras
2 numpy numpy
3 matplotlib matplotlib
4 sklearn scikit-learn

パッケージのインストール

●ライブラリを追加したいので、それに適合したパッケージを追加します

  1. anacondaを起動し、[Envionments] -> [python37]をクリック

●kerasパッケージをインストールします

  1. [Installed]を[All]に変更
  2. 検索BOXに[keras]と入力し、kerasパッケージを検索
  3. [keras]のチェックボックスをONに設定
  4. 右下の[Apply]をクリックして適応させる prog003.png
  • Install PackagesのメッセージBOXが出たら、[Apply]をクリックして、kerasパッケージをインストール prog004.png

●numpyパッケージをインストールします

  1. 検索BOXに[numpy]と入力し、numpyパッケージを検索
  2. numpyパッケージがInstallされていることを確認できました。

kerasはnumpyを使用するため、依存関係からkerasパッケージのインストール時に、numpyも自動的にインストールされていました。ということで、numpyのインストール作業は割愛できました。
prog005.png

●matplotlibパッケージをインストールします

prog006.png

  1. 検索BOXに[matplotlib]と入力し、matplotlibパッケージを検索
  2. [matplotlib]のチェックボックスをONに設定
  3. 右下の[Apply]をクリックして適応させる
  4. Install PackagesのメッセージBOXが出たら、[Apply]をクリックして、matplotlibパッケージをインストール

●scikit-learnパッケージをインストールします

  1. 検索BOXに[scikit-learn]と入力し、scikit-learnパッケージを検索
  2. [scikit-learn]のチェックボックスをONに設定
  3. 右下の[Apply]をクリックして適応させる
  4. Install PackagesのメッセージBOXが出たら、[Apply]をクリックして、scikit-learnパッケージをインストール

※sklearnライブラリは、scikit-learnパッケージに入っています。
prog007.png

プログラム実行

  • エラーが全てなくなっていることを確認します。
  1. [Problems]をクリック
  2. Problemsに!赤丸が出ていないことを確認

prog009.png
prog010.png

  • 右上の「」をクリックしてプログラムを実行してください。 prog011.png

出力結果

  • きちんとできると、以下のような結果が得られます。
  • 今回は「Test accuracy: 0.9359999895095825」と結果が得られたので、認識率は93.4%でした。
C:\Users\xxxx\anaconda3\envs\python37\python.exe C:/Users/xxxx/PycharmProjects/mnist_sample/qiita.py
Using TensorFlow backend.

●ネットワーク定義
2020-08-06 11:36:11.346263: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX AVX2
Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_1 (Conv2D)            (None, 28, 28, 16)        160       
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 28, 28, 32)        4640      
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 14, 14, 32)        0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 14, 14, 32)        0         
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 14, 14, 32)        9248      
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 7, 7, 32)          0         
_________________________________________________________________
flatten_1 (Flatten)          (None, 1568)              0         
_________________________________________________________________
dense_1 (Dense)              (None, 128)               200832    
_________________________________________________________________
dense_2 (Dense)              (None, 10)                1290      
=================================================================
Total params: 216,170
Trainable params: 216,170
Non-trainable params: 0
_________________________________________________________________

●学習スタート
Train on 60000 samples, validate on 10000 samples
Epoch 1/5
2020-08-06 11:36:12.480915: W tensorflow/core/framework/cpu_allocator_impl.cc:81] Allocation of 602112000 exceeds 10% of system memory.
2020-08-06 11:36:14.075159: W tensorflow/core/framework/cpu_allocator_impl.cc:81] Allocation of 602112000 exceeds 10% of system memory.

 6000/60000 [==>...........................] - ETA: 36s - loss: 2.3063 - accuracy: 0.0653
12000/60000 [=====>........................] - ETA: 32s - loss: 2.2858 - accuracy: 0.1563
18000/60000 [========>.....................] - ETA: 29s - loss: 2.2630 - accuracy: 0.2346
24000/60000 [===========>..................] - ETA: 24s - loss: 2.2374 - accuracy: 0.2971
30000/60000 [==============>...............] - ETA: 20s - loss: 2.2083 - accuracy: 0.3415
36000/60000 [=================>............] - ETA: 16s - loss: 2.1742 - accuracy: 0.3779
42000/60000 [====================>.........] - ETA: 12s - loss: 2.1342 - accuracy: 0.4095
48000/60000 [=======================>......] - ETA: 8s - loss: 2.0883 - accuracy: 0.4363 
54000/60000 [==========================>...] - ETA: 4s - loss: 2.0373 - accuracy: 0.4610
60000/60000 [==============================] - 44s 733us/step - loss: 1.9787 - accuracy: 0.4864 - val_loss: 1.3384 - val_accuracy: 0.7674
Epoch 2/5

 6000/60000 [==>...........................] - ETA: 39s - loss: 1.3002 - accuracy: 0.7305
12000/60000 [=====>........................] - ETA: 37s - loss: 1.2238 - accuracy: 0.7381
18000/60000 [========>.....................] - ETA: 33s - loss: 1.1505 - accuracy: 0.7432
24000/60000 [===========>..................] - ETA: 27s - loss: 1.0788 - accuracy: 0.7513
30000/60000 [==============>...............] - ETA: 23s - loss: 1.0145 - accuracy: 0.7597
36000/60000 [=================>............] - ETA: 18s - loss: 0.9617 - accuracy: 0.7652
42000/60000 [====================>.........] - ETA: 14s - loss: 0.9165 - accuracy: 0.7698
48000/60000 [=======================>......] - ETA: 9s - loss: 0.8742 - accuracy: 0.7754 
54000/60000 [==========================>...] - ETA: 4s - loss: 0.8390 - accuracy: 0.7804
60000/60000 [==============================] - 50s 831us/step - loss: 0.8084 - accuracy: 0.7856 - val_loss: 0.4861 - val_accuracy: 0.8541
Epoch 3/5

 6000/60000 [==>...........................] - ETA: 41s - loss: 0.4924 - accuracy: 0.8445
12000/60000 [=====>........................] - ETA: 36s - loss: 0.4970 - accuracy: 0.8453
18000/60000 [========>.....................] - ETA: 32s - loss: 0.5020 - accuracy: 0.8486
24000/60000 [===========>..................] - ETA: 28s - loss: 0.5005 - accuracy: 0.8508
30000/60000 [==============>...............] - ETA: 23s - loss: 0.4866 - accuracy: 0.8547
36000/60000 [=================>............] - ETA: 19s - loss: 0.4774 - accuracy: 0.8578
42000/60000 [====================>.........] - ETA: 14s - loss: 0.4730 - accuracy: 0.8603
48000/60000 [=======================>......] - ETA: 9s - loss: 0.4721 - accuracy: 0.8622 
54000/60000 [==========================>...] - ETA: 4s - loss: 0.4641 - accuracy: 0.8648
60000/60000 [==============================] - 52s 862us/step - loss: 0.4574 - accuracy: 0.8666 - val_loss: 0.3624 - val_accuracy: 0.9004
Epoch 4/5

 6000/60000 [==>...........................] - ETA: 44s - loss: 0.3941 - accuracy: 0.8850
12000/60000 [=====>........................] - ETA: 40s - loss: 0.3863 - accuracy: 0.8882
18000/60000 [========>.....................] - ETA: 34s - loss: 0.3731 - accuracy: 0.8912
24000/60000 [===========>..................] - ETA: 29s - loss: 0.3659 - accuracy: 0.8943
30000/60000 [==============>...............] - ETA: 25s - loss: 0.3545 - accuracy: 0.8971
36000/60000 [=================>............] - ETA: 20s - loss: 0.3461 - accuracy: 0.8987
42000/60000 [====================>.........] - ETA: 15s - loss: 0.3417 - accuracy: 0.9001
48000/60000 [=======================>......] - ETA: 10s - loss: 0.3421 - accuracy: 0.9008
54000/60000 [==========================>...] - ETA: 5s - loss: 0.3367 - accuracy: 0.9023 
60000/60000 [==============================] - 52s 874us/step - loss: 0.3332 - accuracy: 0.9033 - val_loss: 0.2740 - val_accuracy: 0.9225
Epoch 5/5

 6000/60000 [==>...........................] - ETA: 44s - loss: 0.2830 - accuracy: 0.9168
12000/60000 [=====>........................] - ETA: 39s - loss: 0.2939 - accuracy: 0.9151
18000/60000 [========>.....................] - ETA: 35s - loss: 0.2872 - accuracy: 0.9168
24000/60000 [===========>..................] - ETA: 30s - loss: 0.2782 - accuracy: 0.9193
30000/60000 [==============>...............] - ETA: 25s - loss: 0.2782 - accuracy: 0.9188
36000/60000 [=================>............] - ETA: 20s - loss: 0.2733 - accuracy: 0.9200
42000/60000 [====================>.........] - ETA: 15s - loss: 0.2686 - accuracy: 0.9217
48000/60000 [=======================>......] - ETA: 10s - loss: 0.2684 - accuracy: 0.9222
54000/60000 [==========================>...] - ETA: 4s - loss: 0.2654 - accuracy: 0.9233 
60000/60000 [==============================] - 52s 872us/step - loss: 0.2634 - accuracy: 0.9236 - val_loss: 0.2180 - val_accuracy: 0.9360

●検証結果

   32/10000 [..............................] - ETA: 5s
  320/10000 [..............................] - ETA: 2s
  608/10000 [>.............................] - ETA: 2s
  928/10000 [=>............................] - ETA: 1s
 1248/10000 [==>...........................] - ETA: 1s
 1568/10000 [===>..........................] - ETA: 1s
 1920/10000 [====>.........................] - ETA: 1s
 2272/10000 [=====>........................] - ETA: 1s
 2624/10000 [======>.......................] - ETA: 1s
 2976/10000 [=======>......................] - ETA: 1s
 3328/10000 [========>.....................] - ETA: 1s
 3680/10000 [==========>...................] - ETA: 1s
 4032/10000 [===========>..................] - ETA: 1s
 4384/10000 [============>.................] - ETA: 1s
 4736/10000 [=============>................] - ETA: 0s
 5088/10000 [==============>...............] - ETA: 0s
 5408/10000 [===============>..............] - ETA: 0s
 5728/10000 [================>.............] - ETA: 0s
 6048/10000 [=================>............] - ETA: 0s
 6368/10000 [==================>...........] - ETA: 0s
 6560/10000 [==================>...........] - ETA: 0s
 6816/10000 [===================>..........] - ETA: 0s
 7104/10000 [====================>.........] - ETA: 0s
 7392/10000 [=====================>........] - ETA: 0s
 7680/10000 [======================>.......] - ETA: 0s
 8000/10000 [=======================>......] - ETA: 0s
 8320/10000 [=======================>......] - ETA: 0s
 8640/10000 [========================>.....] - ETA: 0s
 8960/10000 [=========================>....] - ETA: 0s
 9280/10000 [==========================>...] - ETA: 0s
 9600/10000 [===========================>..] - ETA: 0s
 9920/10000 [============================>.] - ETA: 0s
10000/10000 [==============================] - 2s 196us/step

batch_size =  6000
epochs =  5
Test loss: 0.21799209741055967
Test accuracy: 0.9359999895095825

●混同行列(コンフュージョンマトリックス) 横:識別結果、縦:正解データ
[[ 966    0    1    1    0    1    6    1    4    0]
 [   0 1108    4    2    0    0    3    1   17    0]
 [  12    2  954   18    7    0    7    8   21    3]
 [   2    2    7  938    0   24    0   11   19    7]
 [   1    2    4    1  908    0   10    3    5   48]
 [   5    1    3   18    0  834    9    2   14    6]
 [  18    4    2    2    6   14  906    2    4    0]
 [   1    5   26    7    7    1    0  916    4   60]
 [  10    0    5   23    9   18    8    4  878   19]
 [  10    5    3   13    8    6    0    7    6  951]]

Process finished with exit code 0

正解率

  • 学習回数を増やす度に、正解率がどんどん上昇しているのが分かると思います。 exe001.PNG

損失関数結果

  • 学習回数を増やす度に、損失率がどんどん下降しているのが分かると思います。 exe002.PNG

正誤表

  • 検証データは10,000枚ありますが、全部出力させると大変なので、最初の100枚まで結果を出力させています。
  • 赤色線で示したものが、AIが認識誤りを起こしたものです。
  • 枠内右下にAIが認識した数字を記載しています。 exe003.PNG

以上

  • お疲れ様でした!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

0からはじめる「Python AIプログラミング」 for windows

はじめに

ここでは、windows10を持っていて、AIプログラミングを「手っ取り早く動かしてイメージをつかみたいと思っている方」向けの手順を説明いたします。

初心者向けのもので、目的は開発環境を整えてプログラム (プログラミングはしません。コピペでOKです^^;) を実行し、「ほぉ、AIプログラミングってこんな感じか~」というところがGOALです。

どんなことをやるか

まず手書きの数字0~9とその正解ラベル(0~9)がセットになった教師データ60,000枚AIに学習させます。その後、手書きの数字0~9とその正解ラベル(0~9)がセットになった検証データ10,000枚AIに検証させて、認識率や検証データの手書き数字がAIによって0~9のどの数字と認識したのかを試してみます。

(1) 教師データ(60,000枚)
手書き画像を正解ラベルに沿って、0~9の数字として覚えさせる。
intro002.png

ai_image_gazou_ninshiki1.png

(2) 検証データ(10,000枚)
手書き画像をAIにより0~9の数字に分類(クラス分類)させ、正解データと比較し当たったかどうかを判定。
intro003.png

ai_image_gazou_ninshiki2.png

※上記データは プログラム実行時に MNISTデータベース から自動的にダウンロードされますので、画像ファイルの収集正解ラベリング(画像データと正解ラベルの結びつけ)等の前準備は一切不要となっています。

開発環境インストール

まず開発に必要なものをインストールします。
開発環境に必要なソフトウェアは以下の2つです。

No. 開発環境 ソフトウェア 今回使用したバージョン 説明
1 python言語 Anaconda Anaconda Navigator 1.9.12 Anacondaは、Python言語の無料のオープンソースディストリビューションであり、パッケージ管理とデプロイメントの簡略化を狙ったものです。大変便利です。
2 IDE(統合開発環境) pycharm Community 2020.2版 コード解析やグラフィカルなデバッガが可能です。Community Editionは無料です。

ちなみにAIを動かすのに Keras というオープンソースのニューラルネットワークライブラリを使用します。

作業フロー

下記の表のNo.1 → 4 の順で実行していってください。

No. 作業概要 作業内容 説明
1 Anaconda設定 ①Download
②Install
③アプリ設定
Kerasというオープンソースのニューラルネットワークライブラリを動かすため、Python言語を使えるようにします。
2 Pycharm設定 ①Download
②Install
③アプリ設定
python言語での開発を簡単にし易くするため、統合開発環境を使えるようにします。
3 PythonによるAIプログラミング ①AIプログラミング
②ライブラリ追加
③AIプログラミング実行
安心してください。プログラミングはしません。プログラムはコピペでOKです。
4 目的達成 思いにふける お疲れ様でした!

以上

  • お疲れ様でした!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

・<Slack>Slackに通知する関数を書いて、いつでも引用できるようにする(Python)

結論

結論
#Slackを定義する
slack_url = "https://hooks.slack.com/services/XXXXXXXXXX"
slack = slackweb.Slack(url=slack_url)

#メッセージを定義する
message = "hello!"

#メッセージを送信する
slack.notify(text=message))

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

続【Python】英文PDF(に限らないけど)をDeepLやGoogle翻訳で自動で翻訳させてテキストファイル、いやHTMLにしてしまおう。

前回の内容と今回やったこと

前回の記事【Python】英文PDF(に限らないけど)をDeepLやGoogle翻訳で自動で翻訳させてテキストファイルにしてしまおう。
では翻訳結果をテキストファイルに出力しましたが、翻訳前の文章と横に並べて見比べられたら便利だと思いませんか。
まだ改善の余地はありますがHTMLで実現しました。
image.png
例に使った論文

ごちゃごちゃと盛り付けたのでコードが汚いですがご容赦ください(もとからか)。

8/7追記

HTML表示時における
対応する英文or和文への、ハイライト機能・ジャンプ機能
ダークモード
を追加しました。また、
非ダークモード時の色合いも優しくしました。

逆翻訳(日本語 → 英語)に対応しました。


例に使った文

コード

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
import time
import pyperclip as ppc

DRIVER_PATH = 'chromedriver.exe'

options = Options()
options.add_argument('--disable-gpu')
options.add_argument('--disable-extensions')
options.add_argument('--proxy-server="direct://"')
options.add_argument('--proxy-bypass-list=*')
options.add_argument('--start-maximized')


def parse_merge(text, n=4900, m=1, inv=False):
    sentences = []
    sentence = ""
    for j, i in enumerate(" ".join(
            text.split("\r\n")).split(". " if inv == False else "。")):
        if i in ("", " ", "."): continue
        if (len(sentence) + len(i) > n) or (j % m == 0):
            sentences.append(sentence)
            sentence = ""
        sentence += i + ("." if inv == False else "。")
    sentences.append(sentence)
    return sentences


def TranslateFromClipboard(tool, write, filename, isPrint, html, title,
                           sentence_cnt, inv):
    driver = webdriver.Chrome(executable_path=DRIVER_PATH,
                              chrome_options=options)
    url = 'https://www.deepl.com/ja/translator' if tool == "DeepL" else f'https://translate.google.co.jp/?hl=ja&tab=TT&authuser=0#view=home&op=translate&sl=auto&tl={"en" if inv else "ja"}'
    driver.get(url)
    if tool == "DeepL":
        textarea = driver.find_element_by_css_selector(
            '.lmt__textarea.lmt__source_textarea.lmt__textarea_base_style')
    elif tool == "GT":
        textarea = driver.find_element_by_id('source')
    en = parse_merge(ppc.paste(), m=sentence_cnt, inv=inv)
    ja = []
    for sentence in en:
        if sentence == "":
            ja.append("")
            continue
        cbText = ppc.paste()
        ppc.copy(sentence)
        textarea.send_keys(Keys.CONTROL, "v")
        ppc.copy(cbText)
        transtext = ""
        while transtext == "":
            time.sleep(1)
            if tool == "DeepL":
                transtext = driver.find_element_by_css_selector(
                    '.lmt__textarea.lmt__target_textarea.lmt__textarea_base_style'
                ).get_property("value")
            elif tool == "GT":
                try:
                    time.sleep(1)
                    transtext = driver.find_element_by_css_selector(
                        '.tlid-translation.translation').text
                except:
                    pass
        if isPrint: print(transtext)
        ja.append(transtext)
        textarea.send_keys(Keys.CONTROL, "a")
        textarea.send_keys(Keys.BACKSPACE)
    driver.quit()
    if write:
        with open(filename + ".txt", "w", encoding='UTF-8') as f:
            f.write("\n".join(ja))
    if html:
        eng = ""
        jpn = ""
        for i, ej in enumerate(zip(en, ja)):
            eng += f'<br><a id="e{i}" href="#j{i}" onmouseover="over(' + f"'j{i}'" + ')" onmouseout="out(' + f"'j{i}'" + f')">{ej[0]}</a><br>'
            jpn += f'<br><a id="j{i}" href="#e{i}" onmouseover="over(' + f"'e{i}'" + ')" onmouseout="out(' + f"'e{i}'" + f')">{ej[1]}</a><br>'
        with open(filename + ".html", "w", encoding='UTF-8') as f:
            f.write(
                f'<h1 align="center">{title}</h1>\n<input id="btn-mode" type="checkbox">\n<hr>\n<body>\n<div class="parent">\n<div id="en">\n{eng}\n</div>\n<div id="ja">\n{jpn}\n</div>\n</div>'
                +
                '<style>\n:root {\n--main-text: #452b15;\n--main-bg: #f8f1e2;\n--highlight-text: #db8e3c;\n}\n:root[theme="dark"] {\n--main-text: #b0b0b0;\n--main-bg: #121212;\n--highlight-text: #fd8787;\n}\nh1 {\ncolor: var(--main-text);\n}\ninput {\nposition: absolute;\ntop: 1%;\nright: 1%;\n}\n#en {\nwidth: 45%;\nheight: 90%;\npadding: 0 2%;\nfloat: left;\nborder-right:1px solid #ccc;\nmargin: 1%;\noverflow: auto;\n}\n#ja {\nwidth: 45%;\nheight: 90%;\nfloat: right;\nmargin: 1%;\noverflow: auto;\n}\na,\na:hover,\na:visited,\na:link,\na:active {\ncolor: var(--main-text);\ntext-decoration: none;\n}\nbody {\nbackground-color: var(--main-bg);\n}\n</style>\n<script>\nvar a = document.getElementsByTagName("a");\nfunction over(e) {\ndocument.getElementById(e).style.color = getComputedStyle(document.getElementById(e)).getPropertyValue("--highlight-text");\n}\nfunction out(e) {\ndocument.getElementById(e).style.color = getComputedStyle(document.getElementById(e)).getPropertyValue("--main-text");\n}\nconst btn = document.querySelector("#btn-mode");\nbtn.addEventListener("change", () => {\nif (btn.checked == true) {\ndocument.documentElement.setAttribute("theme", "dark");\n} else {\ndocument.documentElement.setAttribute("theme", "light");\n}\nfor (var i = 0; i < a.length; i++) {\na[i].style.color = getComputedStyle(a[i]).getPropertyValue("--main-text");\n}\n});\n</script>\n</body>'
            )


if __name__ == "__main__":
    args = [
        "DeepL", False, "translated_text.txt", True, False, "EN ↔ JP", 1, False
    ]
    if input("1. 英語 → 日本語    2. 日本語 → 英語   ") == "2": args[7] = True
    if input("1. DeepL 2.GoogleTranslate  ") == "2": args[0] = "GT"
    if input("翻訳結果を書き出しますか? y/n  ") == "y":
        case = input("1. txt 2. HTML 3. both    ")
        if case == "1":
            args[1] = True
            format_ = ".txt"
        elif case == "2":
            args[4] = True
            format_ = ".html"
        elif case == "3":
            args[1], args[4] = True, True
            format_ = ".txt/.html"
        filename = input(
            f"出力ファイルにつける名前を入力してください(デフォルトは'translated_text{format_}')  ")
        if filename:
            args[2] = filename
        if case == "2" or case == "3":
            title = input("(論文の)タイトルを入力してください   ")
            if title:
                args[5] = title
    try:
        args[6] = int(
            input("何文ずつ翻訳しますか?(デフォルトは1文ずつ。小さいほど出力がきれいで、大きいほど早くなります。)   "))
    except:
        pass
    if input("翻訳経過をここに表示しますか? y/n  ") == "n":
        args[3] = False
    input("準備ができたらEnterを押してください")
    TranslateFromClipboard(*args)

読みやすさはかなり上がりましたが、1文ずつ翻訳しているので全文翻訳するのにかなり時間がかかります(実行時の選択でまとめて翻訳することも出来ます)。

まとめ

HTMLやCSSはズブの素人なのでこうしたらもっと良くなる!という点があればご教示いただけると嬉しいです。

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

Nginx+uWSGI+Flask+Ubuntuで作る本格サーバー(実装編)

インストール編からの続き

各アプリケーションのざっくりとした説明とインストールは
前回の記事を参照

Flask

以下の構造になるようなファイルを作っていく

flask_web_application/
├── application/
│   ├── __init__.py
│   └── views.py
└── server.py

まずappというFlaskの本体を作成

__init__.py
from flask import Flask

app = Flask(__name__)

import application.views

ルートディレクトリにアクセスしたら
"Hello, World"とだけ返す

views.py
from application import app

@app.route('/')
def show_entries():
    return "Hello World"

uWSGIが実行するファイル
ようするにここから処理がスタート

server.py
from application import app

if __name__ == "__main__":
    app.run(host='0.0.0.0')

ちなみに今回は/home/ubuntu/application/に作ったが
他のディレクトリの場合は
uWSGIを実行するユーザーにchownで権限を渡し
chmodで権限を「775」あたりにしておくこと
後々説明するが今回はuWSGIを

グループ名:www-data
ユーザー名:www-data

で実行している

uWSGI

まずuwsgi.iniを作成
uwsgi.iniはuwsgiを起動するときに読み込む設定ファイル

起動時に読み込むだけのものなのでどこに置いてもいいが
わかりやすく今回作っているFlaskアプリケーション
の直下にでも置いとく

uwsgi.ini
[uwsgi]
base = /home/ubuntu/application/flask_web_application

module = server:app

virtualenv = /home/ubuntu/py36_flask_server

pythonpath = %(base)

callable = app

uid = www-data
gid = www-data
master = true
processes = 1
threads = 1
socket = /tmp/uwsgi.sock
chmod-socket = 666
vacuum = true
die-on-term = true
thunder-lock = true

主要な部分だけ

変数 説明
base アプリケーションのディレクトリ
module app = Flask(__name__)で作ったappのこと
virtualenv Pythonの仮想環境のディレクトリ
pythonpath baseと同じ
callable 呼び出すモジュール(app)
uid ユーザー名で実行
gid グループ名で実行
processes 環境とアプリケーションによるがコア数がおすすめ
threads Flaskが使うスレッドの数、Flaskをマルチスレッドで実行すること自体おすすめしない(GIL問題)
socket UNIXドメインソケット

uid、gidで指定するユーザー名・グループ名はあらかじめ作っておく
www-dataなら既に存在しているはず
指定しないとrootで実行することになるのでセキュリティ上あまりよろしくない

そのた諸々、参考になる記事
https://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html
https://qiita.com/yasunori/items/64606e63b36b396cf695
https://qiita.com/wapa5pow/items/f4326aed6c0b63617ebd
https://qiita.com/11ohina017/items/da2ae5b039257752e558

uWSGIをサービスに追加

$ sudo vi /etc/systemd/system/uwsgi.service

#下記を追加
[Unit]
Description=uWSGI
After=syslog.target

[Service]
ExecStart=/home/ubuntu/py36_flask_server/bin/uwsgi --ini /home/ubuntu/application/flask_web_application/uwsgi.ini
# Requires systemd version 211 or newer
RuntimeDirectory=uwsgi
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

ExecStartに仮想環境にインストールしたuWSGIを
--iniに先ほど作った設定ファイルを指定します

サービスにuWSGIを追加

$ sudo systemctl enable uwsgi

Nginx

デフォルトのNginxウェルカムページを消す

conf.d/default.confがあるなら削除

$ sudo rm /etc/nginx/conf.d/default.conf

/nginx/sites-enabled/defaultがあるなら削除

$ sudo rm /etc/nginx/sites-enabled/default

これでデフォルトサイトは消える

sites-availableとsites-enabledがない場合作成しておく

$ sudo mkdir /etc/nginx/sites-available
$ sudo mkdir /etc/nginx/sites-enabled

sites-available と sites-enabledは結局なんなのか

基本的には一つのサーバーで複数のドメインを使うためのもので
管理がしやすくなる

ただ今回は別に複数のドメインを使うわけではないのだが
Ubuntu+Nginx+uWSGIという構造上
conf.dを使うより安定する

使い方は
・sites-availableにドメイン別の設定ファイルを作成
・sites-enabledにsites-availableのシンボリックリンクを張る

という至って簡単なもの
シンボリックリンクはいわゆるショートカット

/etc/nginx/nginx.confのhttpブロック部分の
/etc/nginx/conf.d/*.confを書き換える

$ sudo vi /etc/nginx/nginx.conf

http{
…
include /etc/nginx/conf.d/*.conf;
}

http{
…
include /etc/nginx/sites-enabled/*;
}

/etc/nginx/sites-available/myapplication.confを追加

$ sudo vi /etc/nginx/sites-available/myapplication.conf

# 下記を追加
server {
    listen       80;
    root /var/www/html;
    location / { try_files $uri @yourapplication; }
    location @yourapplication {
        include uwsgi_params;
        uwsgi_pass unix:///tmp/uwsgi.sock;
    }
}

ざっくり説明するとtry_filesでURLそのままを
rootから検索、なかったらuWSGIに(URLそのままで)内部リダイレクトするという構造
rootは特になにも入っていない
全部uWSGIに丸投げしてもいいのだけど公式は上記みたいになっている

uwsgi_paramsはuWSGIの設定ファイル
unix:///tmp/uwsgi.sockはUNIXドメインソケット
といわれるものでようするにこれでuWSGIとの通信を行う

他にTCPで接続したりもできるがポートを占領してしまうので
ドメインソケットを使った方がよい

ショートカットを作ればnginxの設定は終わり

$ sudo ln -s /etc/nginx/sites-available/myapplication.conf /etc/nginx/sites-enabled/myapplication

アクセス

ここらへんで一旦システムごと再起動することを推薦
できない場合はNginxだけは再起動する

$ sudo systemctl restart nginx

uwsgiを起動していないなら起動

& sudo systemctl start uwsgi

uwsgiのエラーチェック

$ sudo systemctl status uwsgi

nginxのエラーチェック

$ sudo systemctl status nginx

一通り読んでエラーがなさそうならアクセスしてみる

$ curl http://127.0.0.1
Hello, World

まとめ

基本的に静的ファイルはNginxに処理させた方が早い

間違っているこうした方がよいという箇所があったら教えてください

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

class名をインスタンスを作らずに取得する

概要

クラスオブジェクト自身からclass名を取得する方法が書いてある日本語の記事がなかったので、備忘録として書いておきます。

class名を取得する方法

インスタンスから取得する

ググってもほとんどがこの方法を使って取得していました。

class A:
  pass

a = A()
print(a.__class__.__name__)
>>> A

クラスオブジェクト自身から取得する

Python3.3から実装された__qualname__で取得できます。
追記:B.__name__でも取得できるようです。@shiracamusさん、ありがとうございます。
また、__qualname__はクラスオブジェクト内ではB.__qualname__ではなく単純に__qualname__とだけでも書けます。

class B:
  pass

print(B.__qualname__)
>>> B

# 追記
class C:
  qualname = __qualname__

print(C.__name__)
print(C.qualname)
>>> C
>>> C

おわりに

__qualname__なら、クラスオブジェクト内やクラスオブジェクトを引数に取る関数の中でも取得できますね。
追記:先ほど追記した通り、__name__を使っても取得できるようです。

参考文献

現在のクラスの名前を取得しますか?

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

[cx_Oracle入門](第16回) LOB型の取り扱い

連載目次

連載:cx_Oracle入門 目次

検証環境

  • Oracle Cloud利用
  • Oracle Linux 7.7 (VM.Standard2.1)
  • Python 3.6
  • cx_Oracle 8.0
  • Oracle Database 19.6 (DBCS HP, 2oCPU)
  • Oracle Instant Client 18.5

事前準備

以下のテーブルを作成しておいてください。

SQL> create table sample16(col1 number, col2 blob);

LOB型への参照・更新の基本

データが1GBまでであれば、CLOB型であればstr、BLOBであればbyteとして、VARCHAR2型やRAW型のように特段の考慮なく扱えます。1GBを超える場合は一定量ずつ処理するストリーム処理の形式で処理する必要があります。ストリームを介さない方がパフォーマンスには優れますが、1件は1GBに満たないデータであっても、fetchmany()とかするとあっという間にメモリを食いつぶしますので、メモリリソースとフェッチするデータ量(平均サイズ×レコード件数)との兼ね合い次第では、小規模サイズのLOBであってもストリーム処理を検討して下さい。

小規模なデータの更新

全ケースにて、処理対象のデータ(ファイル)は別途ご用意ください。サンプルソースを自環境で動かしてみる場合は、ソース内のファイル名もしくは用意したファイルの名称のどちらかを修正してください。以下、BLOB型へのINSERTのサンプルです。

sample16a.py
import cx_Oracle

USERID = "admin"
PASSWORD = "FooBar"
DESTINATION = "atp1_low"
SQL = """
    insert into sample16 values(1, :blobdata)
"""

with open('screenshot1.png', 'rb') as f:
    image = f.read()

with cx_Oracle.connect(USERID, PASSWORD, DESTINATION) as connection:
    with connection.cursor() as cursor:
        cursor.execute(SQL, blobdata=image)
        cursor.execute("commit")

小規模なデータの参照

参照は更新ほど簡単ではなく、第9回に解説したoutputtypehandlerを定義する必要があります。以下のサンプルは、先に登録したレコードを参照して、異なる名称のファイルに保存しています。

sample16b.py
import cx_Oracle

def OutputTypeHandler(cursor, name, defaultType, size, precision, scale):
    if defaultType == cx_Oracle.DB_TYPE_BLOB:
        return cursor.var(cx_Oracle.DB_TYPE_LONG_RAW, arraysize=cursor.arraysize)

USERID = "admin"
PASSWORD = "FooBar"
DESTINATION = "atp1_low"
SQL = """
    select col2 from sample16 where col1 = 1
"""

with cx_Oracle.connect(USERID, PASSWORD, DESTINATION) as connection:
    connection.outputtypehandler = OutputTypeHandler
    with connection.cursor() as cursor:
        cursor.execute(SQL)
        lobdata, = cursor.fetchone()
with open('screenshot2.png', 'wb') as f:
    f.write(lobdata)

なお、当然ながら、両ファイルの内容は同じです。

$ cmp screenshot1.png screenshot2.png
$

大規模なデータの更新

BLOBをベースに解説します。おおよそ以下の手順になります。

  1. LOB列にSQL関数のEMPTY_BLOB()で空のロケータを挿入
  2. INSERT文のRETURNING句でLOBロケータをバインド
  3. LOBロケータに対してストリームでデータを流し込む
sample16c.py
import cx_Oracle

USERID = "admin"
PASSWORD = "FooBar"
DESTINATION = "atp1_low"
SQL = """
    insert into sample16 values(2, empty_blob())
    returning col2 into :blobdata
""" # [1.]


with cx_Oracle.connect(USERID, PASSWORD, DESTINATION) as connection:
    with connection.cursor() as cursor:
        blobdata = cursor.var(cx_Oracle.DB_TYPE_BLOB) # [2.]
        cursor.execute(SQL, [blobdata])
        blob, = blobdata.getvalue()
        offset = 1
        bufsize = 65536 # [3.]
        with open('screenshot1.png', 'rb') as f: # [4.]
            while True:
                data = f.read(bufsize)
                if data:
                    blob.write(data, offset)
                if len(data) < bufsize:
                    break
                offset += bufsize
    connection.commit()

以下、コメントの番号に対応しています。

  1. BLOB列にはSQL関数のEMPTY_BLOB()を使用して空のLOBロケータを挿入しています。そして、RETURNING句を使用して、挿入した空のLOBロケータをバインド変数で受けます。
  2. バインド変数に対応するPython側の変数を用意します。Cursor.var()を使用してBLOBであることを指定しています。
  3. バッファのサイズを指定しています。
  4. オープンしたファイルを3.のサイズ分ずつLOBロケータを介してLOB列に書き込んでいます。

UPDATE文でも、SQL文が変わるだけで流れは同じです。LOB列をEMPTY_BLOB()で更新してください。

大規模なデータの参照

BLOBをベースに解説します。おおよそ以下の手順になります。更新よりは簡単です。

  1. SELECTリストのLOB列(LOBロケータ)をバインド
  2. LOBロケータに対してストリームでデータを読み込む
sample16d.py
import cx_Oracle

USERID = "admin"
PASSWORD = "FooBar"
DESTINATION = "atp1_low"
SQL = """
    select col2 from sample16 where col1 = 2
""" # [1.]


with cx_Oracle.connect(USERID, PASSWORD, DESTINATION) as connection:
    with connection.cursor() as cursor:
        cursor.execute(SQL)
        blob, = cursor.fetchone()
        offset = 1
        bufsize = 65536 # [2.]
        with open('screenshot3.png', 'wb') as f: # [3.]
            while True:
                data = blob.read(offset, bufsize)
                if data:
                    f.write(data)
                if len(data) < bufsize:
                    break
                offset += bufsize

以下、コメントの番号に対応しています。

  1. BLOB列をSELECTします。
  2. バッファのサイズを指定しています。
  3. オープンしたファイルに、LOBのデータを2.のサイズ分ずつ書き込んでいます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nginx+uWSGI+Flask+Ubuntuで作る本格サーバー(インストール編)

複雑になりがちなFlaskを用いたサーバーをインストールから

実装は実装編

登場人物

Nginx

Webサーバー
基本的にクライアントからの接続はこいつを
通してから処理される
いわば元請け

uWSGI

アプリケーションサーバー
今回はWebサーバー(Nginx)とWebアプリケーション(Flask)
とを繋ぐ橋渡し役
いわば中間業者

そもそもWSGIとは?

WebServerGatewayInterfaceの略(ウィスキーと読むらしい)
Webアプリケーション(Flask)とWebサーバー(Nginx)間の取り決めのこと
ようするにプロトコル(仕様)

これがないと
例えばあるWebサーバー(A)を使った場合
その仕様に乗っ取ったWebアプリケーション(A')
を使わなければいけないことになる(現に昔はそうしてたらしい)
逆もまたしかり

その仕様を標準化した形がWSGIということ
当たり前だがこれから使うFlaskもWSGIの仕様に乗っ取ってる

Flask

ウェブアプリケーションフレームワーク
実際の処理はこいつが行う
いわば下請け

Pythonの主要なフレームワークとしてDjangoがあるが
Djangoと比べると必要最低限の機能しか搭載してないので
超軽量かつ簡単に実装できてしまう

NetflixやRedditなどが使っている

Virtualenv

Pythonの仮想環境
システムがパッケージで混雑になることを防止
なくても実装はできるがあった方がよい

事前準備

リポジトリをアップデート

$ sudo apt update

パッケージの更新

$ sudo apt upgrade

gcc,gdbなどの開発ツールをインストールしておく(uwsgiのビルドに必要)

$ sudo apt install build-essential

opensslをインストール

$ sudo apt install openssl

pip3をインストール

$ sudo apt install python3-pip

ファイアウォール

ファイアウォールの設定
ubuntu側でもファイアウォールを設定しておく
必要ないと思ったら飛ばしてOK

ufwをインストール

$ sudo apt install ufw

すべてのポートを閉じる

$ sudo ufw default deny

必要なポートは開けておく
自分の場合はSSH(22), http(80)

$ sudo ufw allow 22
$ sudo ufw allow 80

ファイアウォールを有効化

& sudo ufw enable

ステータスを確認

$ sudo ufw status

ちなみにSSHで接続している場合
この順番を守らないと接続できなくなる
可能性があるので注意

Nginx

最新版のnginxを入れる方法は
https://nginx.org/en/linux_packages.html
https://xn--o9j8h1c9hb5756dt0ua226amc1a.com/?p=3100
から

別に最新版を入れる必要はないならインストールまで
飛ばしてOK

PGPキーをダウンロード

$ wget https://nginx.org/keys/nginx_signing.key

キーをインポート

$ sudo apt-key add nginx_signing.key

リポジトリを登録

$ sudo vi /etc/apt/sources.list

# 末尾に追加 bionicの部分はubuntuの開発コード
deb http://nginx.org/packages/ubuntu/ bionic nginx
deb-src http://nginx.org/packages/ubuntu/ bionic nginx

~/ubuntu/bionic nginxのbionicの部分は
Ubuntuのバージョンによって書き換える

バージョン 開発コード
16.04 xenial
18.04 bionic
19.10 eoan
20.04 focal

ubuntuのversion確認方法は

$ cat /etc/lsb-release
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic

リポジトリ情報を更新

sudo apt update

バージョンを確認

$ apt show nginx
Package: nginx
Version: 1.18.0-1~bionic

記事を書いてる時点で1.18.0が安定版らしい

http://nginx.org/en/download.html
で最新版かどうか確認できる

---ここまでが最新版を入れる方法---

インストール

$ sudo apt install nginx

バージョン確認

$ nginx -v
nginx version: nginx/1.18.0

あらかじめサービスに登録しておく

$ sudo systemctl enable nginx
$ sudo systemctl start nginx

アクセスできれば成功

$ curl http://localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

80番ポートが開いているなら
許可されてるIPから

$ curl http://<パブリックIP>

でも確認しておく

Virtualenv

pip3でインストール

$ sudo pip3 install virtualenv

バージョン確認

$ virtualenv --version

環境を作成

$ virtualenv py36_flask_server --python=python3.6

ちなみに指定したバージョンのPythonが入っていない場合
作成に失敗するので事前にインストールしておく

下記で環境に入れればOK

$ . <環境名>/bin/activate
(py36_flask_server) ubuntu@ip-10-0-0-14:~$

uWSGI

virtualenvで作成した環境からpip3でインストール

$pip3 install uwsgi

バージョンを確認

$uwsgi --version

ちなみにVirtualenvではなくAnacondaを使うという手もあった
ただuWSGIをインストールする段階でエラーが多発するので注意

Anacondaを使いたい場合
時間はかかるが解決はするので下記の記事を参考
https://katsuwosashimi.com/archives/300/python-conda-install-uwsgi-failed/

Flask

virtualenvで作成した環境からpipでインストール

$pip3 install flask

以上

まとめ

インストールの段階でだいぶ長くなってしまった

次は実装編

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

【Python, PyPDF2】見開きのPDFを左右に2分割するスクリプト

今回の内容

PyPDF2を使って、見開きでスキャンした資料などのpdfファイルを左右に2分割するスクリプトのメモです。

ソースコード

from PyPDF2 import PdfFileWriter, PdfFileReader
import copy


def split_pdf(input_pdf,  output_filename):
    pdf_file = PdfFileReader(open(input_pdf, 'rb'))
    marged = PdfFileWriter()
    page_num = pdf_file.getNumPages()

    for i in range(0, page_num):
        page_L = pdf_file.getPage(i)
        page_R = copy.copy(page_L)

        (w, h) = page_L.mediaBox.upperRight

        page_L.mediaBox.upperRight = (w/2, h)
        marged.addPage(page_L)

        page_R.mediaBox.upperLeft = (w/2, h)
        marged.addPage(page_R)

    outputStream = open(output_filename, "wb")
    marged.write(outputStream)
    outputStream.close()


def sample():
    input_pdf = "input.pdf"
    output_filename = "output.pdf"
    split_pdf(input_pdf, output_filename)


if __name__ == "__main__":
    sample()

使い方

split_pdf()に対して、分割するpdfファイルと、出力する際のファイル名を与えることでpdfファイルを2分割します。

簡単な解説

一つのページを左ページ用と右ページ用に2つ用意します。(page_L, page_R)

ページは左下を始点とするので、右上の座標から横幅と高さを取得します。(w, h)

左ページには右側の、右ページには左側の座標を指定することで、それを基準にそれぞれ切り取ります。

今回は、左ページには右上の座標を (w/2, h)、右ページには左上の座標を (w/2, h)として指定し、左右に2分割しています。

蛇足、右ページについてはpage_R.mediaBox.lowerLeft = (w/2, 0)とした方が分かりやすいのかもしれません。

使い方の例

自分は以下のように使っています。追加、変更分のみ書きます。

import sys, os

def main():
    argv = [sys.argv[i] for i in range(1,len(sys.argv))]
    for input_pdf in argv:
        basename_without_ext = os.path.splitext(os.path.basename(input_pdf))[0]
        output_filename = "split_{}.pdf".format(basename_without_ext)
        print("{}を2分割します。".format(os.path.basename(input_pdf)))
        split_pdf(input_pdf, output_filename)


if __name__ == '__main__':
    main()

引数を取得して、for文で回してsplit_pdfに渡します。出力ファイル名は入力ファイル名の先頭にsplit_を付けたものになります。

参考にさせていただいたもの

見開き中央綴じ資料をスキャンしたPDFをページ毎に分割する(Qiita:
@hrb23m)

中心のずれた見開きスキャンPDFを2ページに分割する方法(Python,pypdf)(技術メモ集)

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

Windows の Atom で Pylint

本題

C:\Users\<ユーザー名>\AppData\Roaming\Python\Python38\Scripts\pylint.exe

設定(ctrl + ,)> パッケージ > コミュニティパッケージ > linter-pylint > 設定 > Settings > Executable Pathに上記のパスを設定する。Pythonのバージョンとかは適宜変更してください。

おまけ

概要

Atomのセットアップするたびに躓くので設定手順を書き残しておこうと思いました。

インストール

Atomのパッケージ(linter-pylint)は、Atom内でインストールすればいいと思う。

setup_pylint_atom.sh
pip install pylint
apm install linter-pylint

環境

  • Windows Home 10 2004
  • Atom 1.49.0
    • linter-pylint 2.1.1
  • Python 3.8.5
    • python.org からダウンロード
    • 全ユーザーにインストール
    • PATH追加済み
    • pylint 20.2.1

さいごに

解決しましたか?

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

データの水増し(DataAugmentation)をopenCVで

はじめに

8月に入り激アツな日々皆さんいかがお過ごしですか?
私はいまにも溶けそうな勢いでありますが米津玄師の新曲を聞きながら
冷房の効いた昼夜がわからない部屋でコーディングをしてるわけですが

皆さん夏と言ったらなんですか?
- 夏祭り?
- 花火?
- かき氷?

いや夏と言ったらDataAugmentationの夏ですよ!!!
ということでopenCVでちゃちゃっとDataAugmentationする方法をメモ代わりに書いときます。

誰向けなの

  • 大量のデータをディレクトリ構造を維持したままDataAugmentationしたい人!
  • とりあえず早くデータを作らないと誰かに怒られる人
  • プログラム組むのが面倒な人!
  • 以下のコマンドで実行
    python3 img_change.py input_directory output_directory
  • python3 img_change.py /data/imageset /data/imagser_change

プログラム

img_change.py
# -*- coding: utf-8 -*-
import numpy as np

import os
import cv2
import sys
import pathlib
import glob
from scipy.signal import qspline1d, qspline1d_eval, qspline2d,cspline2d


if len(sys.argv) != 3:
    print("python3 test.py inputdir outputdir")
    sys.exit()
else:   
    input_path = sys.argv[1]
    file_list = glob.glob(input_path + "/**/**.png", recursive=True)
    for item in file_list:
        split_name = item.split('/')
        output_name = sys.argv[2] + "/" + split_name[-3] + "/" + split_name[-2] + "/" + split_name[-1]
        output_dir = sys.argv[2] + "/" + split_name[-3] + "/" + split_name[-2]
        pathlib.Path(output_dir).mkdir(parents=True,exist_ok=True)
        img = cv2.imread(item, cv2.IMREAD_UNCHANGED)  # read image

        #画像処理部分
        ###画像の輝度を上げる
        chg_img=img*1.2 #輝度が2倍になる


        ###画像のリサイズ
        height = img.shape[0]
        #img.shape[0]*0.5でもとの半分のサイズ
        width = img.shape[1]
        chg_img = cv2.resize(img , (int(width), int(height)))

        ###CLAHE(ヒストグラムができるだけ均等にバラけるように再配分)
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
        chg_img = clahe.apply(img)

        ###ノイズ
        row,col,ch = img.shape
        # 白
        pts_x = np.random.randint(0, col-1 , 1000) #0から(col-1)までの乱数を千個作る
        pts_y = np.random.randint(0, row-1 , 1000)
        img[(pts_y,pts_x)] = (255,255,255) #y,xの順番になることに注意

        # 黒
        pts_x = np.random.randint(0, col-1 , 1000)
        pts_y = np.random.randint(0, row-1 , 1000)
        img[(pts_y,pts_x)] = (0,0,0)

        ###画像を歪める
        chg_img = cv2.flip(img, 1) #水平方向に反転
        chg_img = cv2.flip(img, 0) #垂直方向に反転


        cv2.imwrite(output_name,chg_img )  #write image
  • 上のプログラムでは以下のディレクトリ構造でプログラムを作成している
$ tree
/data
└── val
    ├── A1
    │   │  
    │   ├── 0.png
    │   ├── 1.png
    │   ├── 2.png
    │   └── ....
    ├── B1
    │   │  
    │   ├── 0.png
    │   ├── 1.png
    │   ├── 2.png
    │   └── ....    
    │            
    train
    ├── A1
    │   │  
    │   ├── 0.png
    │   ├── 1.png
    │   ├── 2.png
    │   └── ....
    ├── B1
    │   │  
    │   ├── 0.png
    │   ├── 1.png
    │   ├── 2.png
    │   └── ....    
    │              
    test
    ├── A1
    │   │  
    │   ├── 0.png
    │   ├── 1.png
    │   ├── 2.png
    │   └── ....
    ├── B1
    │   │  
    │   ├── 0.png
    │   ├── 1.png
    │   ├── 2.png
    │   └── ....    
    │            
    └── 
  • よって指定したディレクトリの2つ下のディレクトリの.pngをすべてリストとして取得する感じ

終わりに

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

【Discord.py】Cogをついて理解する


僕がDiscord.pyのCogについて勉強したのでそのメモと、「リファレンス読んでもわからな~い!!」という人がいるのでその人たちのために書きました。


:warning: 注意:これはCogの解説です。Discord.pyをまだマスターしていない人は、先にそちらをマスターしてから見ください。


Cog(コグ)とは??

そもそもCogって何??

Bot開発においてコマンドやリスナー、いくつかの状態を一つのクラスにまとめてしまいたい場合があるでしょう。コグはそれを実現したものです。

Discord.pyのドキュメントに書いてありますが、これを見ても「意味不状態」になる人がいるのではないでしょうか?(僕もそうでしたw)

Cogを応用することでDiscord.pyのファイルを2ファイル以上に分けることができたり、Botを起動中にもファイルを簡単に再読み込みすることができたりします!!

Cogの書き方

コマンド

Cogを使わないときはコマンドを定義するさいに以下のように書きますが、

@bot.command()
async def test(ctx):
    pass #実行内容

Cog内でコマンドを定義するときは以下のように書きます。

@commands.command()
async def test(self,ctx):
    pass #実行内容

違いは@bot.~@commands.~なったのと、引数にselfが追加されたことぐらいですかね(表面的なところは)

イベント

Cogを使わないときはイベントを定義するさいに以下のように書きますが、

@bot.event
async def on_ready():
    pass #実行内容

Cog内でイベントを定義するときは以下のように書きます。

@commands.Cog.listener()
async def on_ready(self):
    pass #実行内容

違いは@bot.event@commands.Cog.listener()になったことと、さっきと同じように引数にselfが追加されたことですね。

変数Botの使い方

Cogを使わないときはBotの名前を表示するさいに以下のように書きますが、

print(bot.user)

Cog内で使うときは以下のようになります。

print(self.bot.user)

違いはselfを書かなければいけなくなったってことぐらいですね。

Cogの追加

同じファイルのとき

まずは同じファイルのCogを呼び出してみましょう。

main.py
import discord
from discord.ext import commands

bot = commands.Bot(command_prefix="!")

class Greetings(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self._last_member = None

    @commands.command()
    async def test(self,ctx):
        await ctx.send("test!")

bot.add_cog(Greetings(bot))

こんな感じになります。
つまり、Cogはbot.add_cog(Class名(bot))で読み込みます!!

別ファイルのとき

.
┣━main.py
┗━sub.py

もし、ファイルディレクトリが上のようになっていてsub.pyにあるClassGreetingsを読み込みたいときは以下のようにすることで読み込めます!!

main.py
import discord
from discord.ext import commands

bot = commands.Bot(command_prefix="!")

import sub

bot.add_cog(sub.Greetings(bot))

まず、import subsub.pyを読み込んで、bot.add_cog(sub.Greetings(bot))sub.py内にあるGreetingsを読み込んでいます!!

上以外のディレクトリの場合

上以外のディレクトリの場合も同じようにimportを使うことで読み込みできます。
詳しくは、「Python 3でのファイルのimportのしかたまとめ」を見てください。

Extensionって何??

Extensionとはでも言いましたが、Botを起動中にもファイルを簡単に再読み込みすることができるようになる機能のことです。

Extensionを使うことで、importを使ってファイル指定をせずに済むようになります。

.
┣━ main.py
┗━ sub.py

ファイルディレクトリが上のようになっている場合、以下のようにすることでsub.pyGreetingsを読み込むことができます!!

main.py
import discord
from discord.ext import commands

bot = commands.Bot(command_prefix="!")

bot.load_extension("sub")
sub.py
import discord
from discord.ext import commands

class Greetings(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self._last_member = None

    @commands.command()
    async def test(self,ctx):
        pass

def setup(bot):
    return bot.add_cog(Greetings(bot))

ファイルの読み込み

bot.load_extension()
Cogファイルを読み込むコード。bot.load_extension("ファイル名")でできます。

.
┣━ main.py
┗━ Cog
 ┗━ sub.py

ファイルディレクトリが上のようになっている場合、bot.load_extension("Cog.sub")で読み込みできます。

Extensionを使う上で最も重要な奴

def setup(bot):
    return bot.add_cog(Greetings(bot))

:warning:これがないとExtensionが使えません!ファイルの一番下に書いといてください。
書き方は

def setup(bot):
    return bot.add_cog(Class(bot))

です。

最後に

わからないことや追加してほしい情報があれば言ってください。


前の記事の最後に広告Botのことを宣伝していました。
ということで、今回はTakkun's Serverを紹介させていただきます。

このサーバーはTakkun#1643が作った雑談サーバー及び、Takkun#1643が作ったBot達のサポートサーバーです。
今後、広告Botのサポートサーバーもこちらに移す予定です!

[参加する]
https://discord.gg/VX7ceJw

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

GoogleColabで日本語BERT事前学習済みモデルをお手軽に試してみる

1.はじめに

 前回、BERTの事前学習済みモデルをファインチューニングしてネガ・ポジ判定タスクをやってみたわけですが、ファインチューニング無しでも当然ながら動きます。

 今回は、GoogleColabで日本語BERT事前学習済みモデルをお手軽に試してみます。

2.セットアップ

 セットアップ手順は、以下の様です。コードは、Google Colabで参照下さい(最後にリンクがあります)。

1) モジュールのインストール
 必要なモジュール( pyknp, transformers )をインストールします。

2) 形態素分析ライブラリーのインストール
 京都大学/黒橋・褚・村脇研究室が提供している「日本語形態素解析システムJUMMAN++」を使用しています。

3) 日本語BERT事前学習済みモデルダウンロード
 京都大学/黒橋・褚・村脇研究室が提供している「BERT日本語Pretrainedモデル」を使用しています。

3.BERTはセンター試験問題を解けるか?

 今回のモデルは日本語Wikipediaを使って事前学習しています。ということは古今東西の様々な知識を穴埋め問題を解く形で勉強しているわけで、それなら大学入試センター試験の穴埋め問題くらい解けるかもしれないと無茶振りしてみることにしました。題材は平成30年度の世界史Bの問題9です。
スクリーンショット 2020-08-06 12.55.39.png
 正解は①の「貴族」と「カエサル」なわけですが、さてBERTはどう解答するでしょうか。

import torch
from transformers import BertTokenizer, BertForMaskedLM, BertConfig
import numpy as np
import textwrap
config = BertConfig.from_json_file('./bert/Japanese_L-12_H-768_A-12_E-30_BPE_transformers/config.json')
model = BertForMaskedLM.from_pretrained('./bert/Japanese_L-12_H-768_A-12_E-30_BPE_transformers/pytorch_model.bin', config=config)
bert_tokenizer = BertTokenizer('./bert/Japanese_L-12_H-768_A-12_E-30_BPE_transformers/vocab.txt',
 do_lower_case=False, do_basic_tokenize=False)
from pyknp import Juman
jumanpp = Juman()

 まず、必要なライブラリーをインポートし、BERTの設定をします。

 BERTへの入力は、単語リストの先頭に[CLS]を入れ、文の区切りに[SEP]を入れ、予測したい単語は[MASK]に置き換える仕様になっているので、それを行う関数を定義します。

# 単語リストへ[CLS],[SEP],[MASK]を追加する関数
def preparation(tokenized_text):

    # [CLS],[SEP]の挿入
    tokenized_text.insert(0, '[CLS]')  # 単語リストの先頭に[CLS]を付ける
    tokenized_text.append('[SEP]')  # 単語リストの最後に[SEP]を付ける 

    maru = []
    for i, word in enumerate(tokenized_text):
        if word =='。' and i !=len(tokenized_text)-2:  # 「。」の位置検出
            maru.append(i)

    for i, loc in enumerate(maru):
        tokenized_text.insert(loc+1+i, '[SEP]')  # 単語リストの「。」の次に[SEP]を挿入する

    # 「□」を[MASK]に置き換え 
    mask_index = []
    for index, word in enumerate(tokenized_text):
        if word =='□':  # 「□」の位置検出
            tokenized_text[index] = '[MASK]'
            mask_index.append(index)

    return tokenized_text, mask_index  

 関数は、単語リストにの先頭に[CLS]を挿入し、最後に[SEP]を付加し、途中は「。」の後に[SEP]を挿入します。そうやって単語位置を決めてから、予測箇所「□」を[MASK]に置き換え、単語リストと[MASK]位置を返します。

 次に、テキストをIDテンソルに変換します。

# テキストをIDテンソルに変換
text = "ギリシア人ポリュビオスは,著書『歴史』の中で,ローマ共和政の国制(政治体制)を優れたものと評価している。彼によれば,その国制には,コンスルという王制的要素,元老院という□制的要素,民衆という民主制的要素が存在しており,これら三者が互いに協調や牽制をしあって均衡しているというのである。ローマ人はこの政治体制を誇りとしており,それは,彼らが自らの国家を指して呼んだ「ローマの元老院と民衆」という名称からも読み取ることができる。共和政期末の内戦を勝ち抜いたかに見えた□でさえも,この体制を壊そうとしているという疑いをかけれ,暗殺されてしまった。"
result = jumanpp.analysis(text)  # 分かち書き
tokenized_text = [mrph.midasi for mrph in result.mrph_list()]  # 単語リストに変換
tokenized_text, mask_index = preparation(tokenized_text)  # [CLS],[SEP],[MASK]の追加
tokens = bert_tokenizer.convert_tokens_to_ids(tokenized_text)  # IDリストに変換
tokens_tensor = torch.tensor([tokens])  # IDテンソルに変換

 テキストを単語リストにし、先程の関数を使って[CLS],[SEP],[MASK]を追加したら、IDリストに変換し、Pytorchが読めるIDテンソルに変換します。

スクリーンショット 2020-08-06 18.47.49.png
 こんな感じに変換を行います。

 それでは、[MASK]箇所を推論(上位5つの候補)します。

# [MASK]箇所を推論(TOP5)
model.eval() 
tokens_tensor = tokens_tensor.to('cuda')
model.to('cuda')
print(textwrap.fill(text, 45))
print()

with torch.no_grad():
  outputs = model(tokens_tensor)
  predictions = outputs[0]

  for i in range(len(mask_index)):
     _, predicted_indexes = torch.topk(predictions[0, mask_index[i]], k=5)
     predicted_tokens = bert_tokenizer.convert_ids_to_tokens(predicted_indexes.tolist())
     print(i, predicted_tokens)

スクリーンショット 2020-08-06 15.35.16.png

 無茶振りでしたが、1つ目の予測にはちゃんと正解の「貴族」が含まれています! 残念ながら2つ目の「カエサル」は正解できませんでしたが、BERT思ったよりやりますね。

4.BERTは文生成ができるか?

 事前学習しかしていないBERTは穴埋め問題2つの文の繋がりしか学習していないので、そのままでは文生成には向かないです。しかし原理上やれないことはないです。

 あるテキストを用意して、先頭の単語に[MASK]を掛け予測をしたら、先頭の単語を予測結果に置き換え、次の単語に[MASK]を掛け予測する、ということを繰り返すとテキストに似た新たな文が生成ができるはずです。

 では、やってみましょう。題材は、「ケネディ大統領がアポロ計画の支援を表明した演説(和訳)」です。
スクリーンショット 2020-08-06 13.28.39.png

# 形態素解析
text = "我々が10年以内に月に行こうなどと決めたのは、それが容易だからではありません。むしろ困難だからです。この目標が、我々のもつ行動力や技術の最善といえるものを集結しそれがどれほどのものかを知るのに役立つこととなるからです。その挑戦こそ、我々が受けて立つことを望み、先延ばしすることを望まないものだからです。そして、これこそが、我々が勝ち取ろうと志すものであり、我々以外にとってもそうだからです。"
result = jumanpp.analysis(text)  # 分かち書き
tokenized_text = [mrph.midasi for mrph in result.mrph_list()]  # 単語リストに変換
tokenized_text, mask_index = preparation(tokenized_text)  # [CLS],[SEP]の追加
tokens = bert_tokenizer.convert_tokens_to_ids(tokenized_text)  # IDリストに変換
tokens_tensor = torch.tensor([tokens])  # IDテンソルに変換

 先程同様、テキストを単語リストにし、定義した関数を使って[CLS],[SEP]を追加したら、IDリストに変換し、Pytorchが読めるIDテンソルに変換します。

 何度も単語予測を行うので、単語を1つ予測する関数を定義します。 

# 1単語予測関数
def predict_one(tokens_tensor, mask_index):

    model.eval()    
    tokens_tensor = tokens_tensor.to('cuda')
    model.to('cuda')

    with torch.no_grad():
      outputs = model(tokens_tensor)
      predictions = outputs[0]

      _, predicted_indexes = torch.topk(predictions[0, mask_index], k=5)
      predicted_tokens = bert_tokenizer.convert_ids_to_tokens(predicted_indexes.tolist())
    return predicted_tokens, predicted_indexes.tolist()

 [MASK]を掛けた単語を予測し、予測した単語とIDを返す関数です。

 そして、文生成をするコードを書きます。

# 文生成
for i in range(1,len(tokens_tensor[0])):
    tmp = torch.tensor(tokens_tensor)  # tokens_tensorをtmpにコピー
    tmp[0, i]=4  # i番目を[mask]に書き換え
    predicted_tokens, predicted_indexes =predict_one(tmp, i)  # [mask]を予測
    if predicted_indexes !=1:  # 予測が[UNK]でなければ
      tokens_tensor[0, i] = predicted_indexes[0]  # 予測IDの[0]番目でtokens_tensorのi番目を上書きする

target_list = tokens_tensor.tolist()[0]  
predict_list = bert_tokenizer.convert_ids_to_tokens(target_list)  
predict_sentence = ''.join(predict_list[1:])

print('------ original_text -------')
print(textwrap.fill(text,45))
print('------ predict_text -------')
print(textwrap.fill(predict_sentence,45))  

 tokens_tensorを一端tmpにコピーして、tmpに順次[MASK]を掛け予測した結果で、tokens_tensorの該当箇所を上書きする、ということを繰り返します。さて、これを実行すると、

スクリーンショット 2020-08-06 15.37.18.png

 オリジナルが「10年以内に月に行こう」と言っているのに、文生成は「1年以内に海外に行くべきだ」と、やたらこじんまりしてしまいました(笑)。文の中身は、ちょっと意味不明な感じです。事前学習だけだと、文生成はあまり上手く行かないようです。

 コード全体は Google Colab で作成し Github に上げてありますので、自分でやってみたい方は、この 「リンク」 をクリックし表示されたシートの先頭にある「Colab on Web」ボタンをクリックすると動かせます。

(参考)
BERT日本語モデルを使って、クリスマスプレゼントに欲しいものを推測してみた
ColabでJUMAN++を使う

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

Azure Speech SDKを用いて、音声からテキストへ変換すーる

はじめに

Azure Speech SDKを用いて、音声からテキストへ変換してみます。

開発環境

  • Windows 10
  • Python 3.6
  • Anaconda
  • Azure Speech SDK

マイクから音声を認識する

1.Azureポータルにログインして、音声サービスを作成します。
image.png

2.作成したリソースへ移動し、キーと場所をコピーしておいてください。
image.png

3.Python 3.6環境を作成します。

conda create -n py36 python=3.6
conda activate py36

4.ライブラリをインストールします。

pip install azure-cognitiveservices-speech

5.プログラムを作成します。

一度だけ音声入力して認識結果を表示するプログラムです。"YourSubscriptionKey"に先ほどコピーしたキーを, "YourServiceRegion"に先ほどコピーした場所を貼り付けてください。日本語を認識したいのでlanguageは"ja-JP"にします。

stt.py
import azure.cognitiveservices.speech as speechsdk

# Creates an instance of a speech config with specified subscription key and service region.
# Replace with your own subscription key and service region (e.g., "westus").
speech_key, service_region, language = "YourSubscriptionKey", "YourServiceRegion", "ja-JP"
speech_config = speechsdk.SpeechConfig(subscription=speech_key, region=service_region)

# Creates a recognizer with the given settings
speech_recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config)

print("Say something...")


# Starts speech recognition, and returns after a single utterance is recognized. The end of a
# single utterance is determined by listening for silence at the end or until a maximum of 15
# seconds of audio is processed.  The task returns the recognition text as result. 
# Note: Since recognize_once() returns only a single utterance, it is suitable only for single
# shot recognition like command or query. 
# For long-running multi-utterance recognition, use start_continuous_recognition() instead.
result = speech_recognizer.recognize_once()

# Checks result.
if result.reason == speechsdk.ResultReason.RecognizedSpeech:
    print("Recognized: {}".format(result.text))
elif result.reason == speechsdk.ResultReason.NoMatch:
    print("No speech could be recognized: {}".format(result.no_match_details))
elif result.reason == speechsdk.ResultReason.Canceled:
    cancellation_details = result.cancellation_details
    print("Speech Recognition canceled: {}".format(cancellation_details.reason))
    if cancellation_details.reason == speechsdk.CancellationReason.Error:
        print("Error details: {}".format(cancellation_details.error_details))

こちらは継続的に音声入力して、認識結果を表示するプログラムです。同様にキーと場所、言語の設定をお願いします。

stt.py
import azure.cognitiveservices.speech as speechsdk
import time

# Creates an instance of a speech config with specified subscription key and service region.
# Replace with your own subscription key and service region (e.g., "westus").
speech_key, service_region, language = "YourSubscriptionKey", "YourServiceRegion", "ja-JP"
speech_config = speechsdk.SpeechConfig(
    subscription=speech_key, region=service_region, speech_recognition_language=language)

# Creates a recognizer with the given settings
speech_recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config)

print("Say something...")

def recognized(evt):
    print('「{}」'.format(evt.result.text))
    # do something

def start(evt):
    print('SESSION STARTED: {}'.format(evt))

def stop(evt):
    print('SESSION STOPPED {}'.format(evt))

speech_recognizer.recognized.connect(recognized)
speech_recognizer.session_started.connect(start)
speech_recognizer.session_stopped.connect(stop)

try:
    speech_recognizer.start_continuous_recognition()
    time.sleep(60)
except KeyboardInterrupt:
    print("bye.")
    speech_recognizer.recognized.disconnect_all()
    speech_recognizer.session_started.disconnect_all()
    speech_recognizer.session_stopped.disconnect_all()

6.下記コマンドを実行し、話しかけてみてください。

python stt.py

認識結果が以下のように表示されます。
image.png

音声ファイル(.wav)から音声を認識する

1.導入方法は上と同様にしてください。

2.プログラムを作成します。

.wavファイルを読み込み、音声認識結果を表示するプログラムです。キーと場所を設定してください。

stt_from_file.py
import azure.cognitiveservices.speech as speechsdk

# Creates an instance of a speech config with specified subscription key and service region.
# Replace with your own subscription key and region identifier from here: https://aka.ms/speech/sdkregion
speech_key, service_region = "YourSubscriptionKey", "YourServiceRegion"
speech_config = speechsdk.SpeechConfig(subscription=speech_key, region=service_region)

# Creates an audio configuration that points to an audio file.
# Replace with your own audio filename.
audio_filename = "aboutSpeechSdk.wav"
audio_input = speechsdk.audio.AudioConfig(filename=audio_filename)

# Creates a recognizer with the given settings
speech_recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config, audio_config=audio_input)

print("Recognizing first result...")

# Starts speech recognition, and returns after a single utterance is recognized. The end of a
# single utterance is determined by listening for silence at the end or until a maximum of 15
# seconds of audio is processed.  The task returns the recognition text as result. 
# Note: Since recognize_once() returns only a single utterance, it is suitable only for single
# shot recognition like command or query. 
# For long-running multi-utterance recognition, use start_continuous_recognition() instead.
result = speech_recognizer.recognize_once()

# Checks result.
if result.reason == speechsdk.ResultReason.RecognizedSpeech:
    print("Recognized: {}".format(result.text))
elif result.reason == speechsdk.ResultReason.NoMatch:
    print("No speech could be recognized: {}".format(result.no_match_details))
elif result.reason == speechsdk.ResultReason.Canceled:
    cancellation_details = result.cancellation_details
    print("Speech Recognition canceled: {}".format(cancellation_details.reason))
    if cancellation_details.reason == speechsdk.CancellationReason.Error:
        print("Error details: {}".format(cancellation_details.error_details))

音声ファイルはcognitive-services-speech-sdkにあるsampledata\audiofiles\aboutSpeechSdk.wavを用います。

3.下記コマンドを実行し、結果を見てみましょう。

python stt_from_file.py

キーと場所が正しくないと下記のようなエラーが出ます。

(py36) C:\Users\good_\Documents\PythonProjects\AzureSpeech>python stt_from_file.py
Recognizing first result...
Speech Recognition canceled: CancellationReason.Error
Error details: Connection failed (no connection to the remote host). Internal error: 1. Error details: 11001. Please check network connection, firewall setting, and the region name used to create speech factory. SessionId: 77ad7686a9d94b7882398ae8b855d903

結果は下記のようになりました。
image.png

52秒あるのですが、最初の一行を認識したら、終了してしまうようです。

4.継続的に読み込み、音声認識するためには、下記のようにします。

stt_from_file.py
import azure.cognitiveservices.speech as speechsdk
import time 

# Creates an instance of a speech config with specified subscription key and service region.
# Replace with your own subscription key and region identifier from here: https://aka.ms/speech/sdkregion
speech_key, service_region = "YourSubscriptionKey", "YourServiceRegion"
speech_config = speechsdk.SpeechConfig(subscription=speech_key, region=service_region)

# Creates an audio configuration that points to an audio file.
# Replace with your own audio filename.
audio_filename = "aboutSpeechSdk.wav"
audio_input = speechsdk.audio.AudioConfig(filename=audio_filename)

# Creates a recognizer with the given settings
speech_recognizer = speechsdk.SpeechRecognizer(speech_config=speech_config, audio_config=audio_input)

print("Recognizing...")

def recognized(evt):
    print('「{}」'.format(evt.result.text))
    # do something

def start(evt):
    print('SESSION STARTED: {}'.format(evt))

def stop(evt):
    print('SESSION STOPPED {}'.format(evt))

speech_recognizer.recognized.connect(recognized)
speech_recognizer.session_started.connect(start)
speech_recognizer.session_stopped.connect(stop)

try:
    speech_recognizer.start_continuous_recognition()
    time.sleep(60)
except KeyboardInterrupt:
    print("bye.")
    speech_recognizer.recognized.disconnect_all()
    speech_recognizer.session_started.disconnect_all()
    speech_recognizer.session_stopped.disconnect_all()

5.再度実行してみましょう。

下記のように継続的に音声認識できているようです!
image.png

お疲れ様でした。

参考

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

Python BeautifulSoupでImportErrorが出る

Pythonを独学で勉強中です。
詳しいことはまだわかりませんが、自分が躓いた現象をメモしていこうと思います。
ちなみにPython3.8.5です

スクレイピングをする際、BeautifulSoupを使うのですがこれで1度ハマりました。

実行したいコード

titleタグを抽出しようとしました。

コード
# html_parser.py
import requests
from bs4 import BeautifulSoup

# 取得したいURL
url = "http://example.com"

# urlを引数に指定して、HTTPリクエストを送信してHTMLを取得
response = requests.get(url)

# 文字コードを自動でエンコーディング
response.encoding = response.apparent_encoding

# HTML解析
bs = BeautifulSoup(response.text, 'html.parser')
title_tag = bs.find('title')

# 抽出したタグのテキスト部分を出力
print(title_tag.text)

実行結果がこちら

BeautifulSoupのImportErrorが出てしまいました。

結果
Traceback (most recent call last):
  File "c:/python/html.py", line 3, in <module>
    from bs4 import BeautifulSoup
  File "C:\Users\*****\AppData\Local\Programs\Python\Python38\lib\site-packages\bs4\__init__.py", line 31, in <module>
    from .builder import builder_registry, ParserRejectedMarkup
  File "C:\Users\*****\AppData\Local\Programs\Python\Python38\lib\site-packages\bs4\builder\__init__.py", line 7, in <module>
    from bs4.element import (
  File "C:\Users\*****\AppData\Local\Programs\Python\Python38\lib\site-packages\bs4\element.py", line 19, in <module>
    from bs4.formatter import (
  File "C:\Users\*****\AppData\Local\Programs\Python\Python38\lib\site-packages\bs4\formatter.py", line 1, in <module>
    from bs4.dammit import EntitySubstitution
  File "C:\Users\*****\AppData\Local\Programs\Python\Python38\lib\site-packages\bs4\dammit.py", line 13, in <module>
    from html.entities import codepoint2name
  File "c:\python\html.py", line 3, in <module>
    from bs4 import BeautifulSoup
ImportError: cannot import name 'BeautifulSoup' from partially initialized module 'bs4' (most likely due to a circular import) (C:\Users\*****\AppData\Local\Programs\Python\Python38\lib\site-packages\bs4\__init__.py)

言われなくとも、下記は実行済みです

pip install beautifulsoup

pip list でも

beautifulsoup4 4.9.1

と確認できます。

ではなぜか....

ファイル名が"html.py"になっていたからです。

Pythonには"html"というパッケージがありそちらを読み込んでしまっていたようです...

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

【インストール不要⁉︎】インストールする前に知っておきたいPythonの話

はじめに

ご覧いただきありがとうございます。
Pythonをインストールするにあたり調べながらやったところ、失敗しました。
Pythonはインストール方がたくさんあり、どれをどうすればいいのー?と調べれば調べるほど分からなくなります。
これからPythonを始める方にどの方法でインストールするのがおすすめか私なりにまとめてみました。

特にプログラミング経験がないがPythonをこれから始めたいと思っている方に有用な記事になるのではないかと思います。

Pythonをインストールする理由

macには標準でPythonがインストールされています。なぜあえてインストールするでしょう。
調べたり、知人に聞いたりして得られた理由が以下の2点です。

  • インストールされているPythonのバージョンが古いから
  • バージョンの管理ができないから

それぞれについて以下で詳しく解説します。

インストールされているPythonのバージョンが古いから

「macにはPython2が標準で入っているのでPython3をインストールします」といったPython3が標準で入っていないような記述を見かけることがあります。しかし、これは間違いで最近のOSにはPython3も標準で入っています
入っているものの、バージョンは3.7.3と少し古め(2020年8月6日時点で3.8.5が最新)。
新しい書き方を使用できないなどの不都合が生じる可能性があります。

バージョンの管理ができないから

標準でインストールされているPythonを使用している場合、使用するPythonのバージョンを自分で選択することができません。
そのため使用したいライブラリが標準で入っているバージョンをサポートしていない場合、OSの更新の際にPythonのバージョンが更新される、もしくはそのライブラリのサポートの範囲が広がるのを待たなくてはなりません。特にライブラリが古い場合、更新されていない可能性もありますよね。標準のPythonが前のバージョンに戻ることはほとんどないと思われるので使用を諦めなくてはいけなくなります。

また、新しいOSでPythonのバージョンが更新されると、自動的に使用するPythonのバージョンが変わることになります。新しいバージョンになると新しい文法が使用できるようになるのと同時に、今までの文法が使用できなくなる可能性もあります。そのため、アプリを開発している途中でPythonのバージョンが変わってしまうとエラーの原因になる可能性があります。

これらがあえてPythonをインストールする理由です。

Pythonをインストールする方法

すでに述べたようにPythonのダウンロード方法はいくつかあります。

  • 公式ページから
  • Homebrewの使用
  • pyenvの使用

それぞれの方法について解説していきます。

公式ページから

image.png
Python公式ページダウンロードページから直接ダウンロードします。そのままですね。
詳しいインストールの方法はこちらの記事を参考にするといいと思います。

この方法でのインストールをおすすめする方

  • Windows使用者の方
  • ターミナルを使用せずにインストールしたい方

インストーラーでインストールすることができるので、視覚的に分かりやすいです。
これ以降に説明する方法はターミナルの操作が必要です。プログラミングをこれから勉強しようと思っている方にとってターミナルの使用は壁が高く感じるのではないでしょうか?そのような方に向いている方法だと思います。
私はWindowsを使用していないので詳しくは分からないのですが、調べているとWindowsの方はこの方法でインストールするのが一般的なようです。

Homebrew?

HomebrewとはmacOS・Linux向けのパッケージ管理システムです。
macOS・Linux使用者向けですがWindows使用者の方でも使用できるようです。
ターミナルに以下を打ち込むことで簡単にインストールできます。

// Homebrewのインストール
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

// Pythonのインストール
brew install python3

HomebrewでPythonをインストールし環境構築を行う場合こちらの記事が参考になると思います。

この方法でのインストールをおすすめする方

  • 最新のバージョンのPythonを使用したい方

Homebrewではバージョンの指定をせずにインストールすることが可能です。
最新のバージョンを使用したい方にとって上の2行をターミナルにコピペするだけですむこの方法が一番楽だと思います。
またPythonのバージョンのアップグレードも簡単に行えます。
Pythonのバージョンアップグレードの方法

pyenv

pyenvとはPythonのバージョンの切り替えを簡単にしてくれるツールです。
PC全体で使用するPythonのバージョンだけでなく、フォルダごとにバージョンを分けることも可能です。
pyenvを使用してPythonをインストールする方法を以前書きましたので、よろしければこちらをご覧ください。

この方法でのインストールをおすすめする方

  • チームで開発を行う方
  • 複数のアプリの開発を行う方

このインストール法をおすすめする方は、Pythonのバージョンを切り替えたい方です。例えばチームで開発を行っていて開発環境を合わせたい、複数アプリの開発を行っていて切り替える必要があるなどです。新しいバージョンのPythonには新しい文法(新しい書き方)が加わると同時に今までの書き方がサポートされなくなる可能性があります。今まで作成したアプリにサポートされなくなる文法が使用されていると、バージョンの更新と同時にいきなり動かなくなってしまいます。使用するバージョンを切り替えることでそのようなエラーを防ぐことが可能です。

これからPythonをはじめる方におすすめする方法

これはmac使用者の方に限られてしまうのですが、ここまで調べて私が思うおすすめの方法はインストールしない!です。
環境構築は難しいです。環境構築とGoogleにうちこむと難しいと予測変換で出てくるほどです(笑)
よく分からないままインストールしていると、別の方法でインストールされたPythonが複数存在することになってしまいわけがわからなくなります。
標準でインストールされているPythonのバージョンもそこまで古くないので、とりあえずPythonに触れてみるのがいいのではないかと思います。

pythonのバージョンを確かめる方法

python3 -V  or python3 --version
 =>Python 3.7.3

python -Vとしてしまうと2.o.oのようにPython2のバージョンが出てきてしまうので注意してください。

最後に

これまでpyenvを使用している方が多く、バージョンの管理もしやすいならpyenvを使うのがベストプラクティスなんだと思っていました。
しかし、新しいバージョンで今までの文法がサポートされなくなることは頻繁にあることではないと思います。
また、ライブラリのバージョンアップでサポートするPythonのバージョンが古いほうに伸びるということもあるようです。Python3がmacに入っていないという記述や、bash使用者向けの記事が今でも多いことなどから今までのやり方を続けている方が多いのかもしれません。

Pythonを勉強し始めてまだ2週間ほどなので理解できていないところも多々あります。
間違い等ありましたらご指摘おねがいします。最後までご覧いただきありがとうございました。

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

PythonのSeleniumで秒速でスクレイピングする方法

セレクタをXPATHで取得

Chromeの拡張機能のxPath Finderを使用し素早くページ上のXPATHを指定する。

image.png

そしてそこにsend_keysなどで入力項目を送る。

以下はSBI証券のログインのスクリプトの抜粋です。

try:
    browser.get('https://www.sbisec.co.jp/ETGate')
    username_field = browser.find_element_by_xpath('/html/body/table/tbody/tr[1]/td[2]/form/div/div/div/dl/dd[1]/div/input')
    username_field.clear()
    username_field.send_keys(username)
    password_field = browser.find_element_by_xpath("/html/body/table/tbody/tr[1]/td[2]/form/div/div/div/dl/dd[2]/div/input")
    password_field.send_keys(password)
    login_button = browser.find_element_by_xpath("/html/body/table/tbody/tr[1]/td[2]/form/div/div/div/p[2]/a/input")
    login_button.click()#click login
    time.sleep(5)

このパターンを使えばSeleniumを使ったプロジェクトが一気に捗ります。

更新履歴

  • 2020/06/06 新規作成
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

anvil-app-serverのデータ管理について

前書き

anvil-app-serverでアプリが動作させられるようになったのはいいのですが、Anvilのサイトで作成したNews Aggregaterアプリを動かそうとしても、すんなりとは動いてくれないようで。。。
ちょっと調査してみました。

前提

環境

  • Ubuntu 20.04 LTS 日本語 Remix
  • Hyper-V マネージャー 10.0.18362.1
  • anvil-app-server v1.1

News Aggregatorアプリのダウンロードと実行

アプリを作成すると、View historyからgit cloneコマンドでダウンロードできます。

"View history"をクリックし、
anvil_newsaggrigator_download_1.png

"Clone with Git"をクリックし、
anvil_newsaggrigator_download_2.png

"git clone ssh://~"の行をコピーしてコマンドで実行。
anvil_newsaggrigator_download_3.png

git clone ssh://XXXX@anvil.works:2222/324IEWAMVBYU2Y77.git News_Aggregater

XXXXにはAnvilサイトに登録したアカウントのメールアドレスが入る。
※二要素認証を有効にしているとSSHパブリックキーを使用しないといけない為、使っている場合は一時的に二段階認証を外しておく。Gitにパブリックキー登録してあればすんなりいけると思いますが。

anvil-app-serverでアプリケーションを起動。

anvil-app-server --app News_Aggregater

http://localhost:3030にブラウザでアクセス。
anvil_app_server_url.png

でいきなりエラー。
anvil_app_server_NewsAggrigator_error.png

問題点

News Aggrigatorアプリでは記事のcategorycategoriesテーブルに予め登録し、そこから選択する方法をとっているのですが、アプリにはテーブルのデータが含まれていないため、「空のデータ」でエラーになる。

anvil-app-serverのPostgreSQLへpgadmin4でアクセス

anvil-app-serverは独自のPostgreSQLサーバーを含んでいるので、そちらにアクセスする。

  • Username : postgres
  • Password : (anvil-app-serverを実行したディレクトリ)/.anvi-data/postgres.password内に記載
  • Port : (anvil-app-serverを実行したディレクトリ)/.anvi-data/db/postmaster.opts内に記載

が、見たところ、アプリで必要なはずのarticlesテーブルもcategoriesテーブルもなし。
anvil_db_tables.png

とりあえず、存在しているテーブルの中身を見ていくと、app_storage_tablesテーブル内にアプリで作成したテーブルの情報を発見。
anvil_db_app_storage_tables.png

また、create-anvil-appコマンドで作成できるtodoアプリを使ってデータの作成を見ていくと、app_storage_dataテーブルにデータが追加されていることが判明。
anvil_db_app_storage_tables.png

つまりどういうことか

ここまでのことから、

  • アプリで使用するテーブルの情報は、app_storage_tablesテーブルにJSON形式で格納される。
  • アプリからデータにアクセスする場合は、app_storage_tablesテーブルの情報を元に、app_storage_dataテーブルのJSON形式のデータにアクセスする。

どんなアプリを作ってもデータ層とロジック層の整合が取れるという観点では、メタテーブルを使うのは考えられなくもないですが、素人の心を挫く仕組みではあります。

その他

どのようなデータ管理なのか、少し見えてきたところで、データのインポートをしてみようと、Anvilのサイトに戻って、データのダウンロードを行ってみました。
が、ダウンロードしたデータもすんなりインポートできる内容ではありません。
今回のNews Aggrigatorのcategoriesテーブルの場合、

ID name
[65114][57547127] entertainment
[65114][57547131] business
[65114][57547132] travel
[65114][57547134] sport

の様になっており、このデータをローカルDBのメタテーブルと整合が取れた形でJSON形式データを書き込まなければなりません。
※IDのカラムは「おそらく」Anvilでホストしているサーバーにデータが乗っている際の情報なのでローカルDBではあまり意味がなさそう。直感的にはテーブルIDとrowIDくらいかなという気はします。

後書き

有償プランであれば、ある程度融通がきく、とか、適切なアドバイスなりツールの利用なりがある、と想像しますが、無料プランでアプリ作って自前でホストして使うには、少々難易度高いと思います。
まあ、有償プランなら独自ドメインも設定できるようなので、そちらの使い方が本道なのでしょう。

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

統計学を0から復習【四則演算】

はじめに

統計学を復習しはじめたので、備忘録として自分なりの解釈をまとめていこうと思います。今回は統計のもっとも基礎的な四則演算についてですが、今後は推定・検定・多変量解析・ベイズ統計まで勉強できたらと思います。統計を復習したいと思っている人や自分の統計に対する理解度を確認したい人の参考になるように心掛けます。

統計学とは

そもそも統計学とは何をするための学問でしょうか。統計に関する有名な逸話としてポアンカレとパン屋の話があります。

その逸話とは以下のようなものです。
ポアンカレは馴染みのパン屋で、重量1000gのパンをよく買っていたそうです。ですが、どうも重さをごまかされていると感じたポアンカレは買ったパンの重さを毎回計ることにしました。
1年後にポアンカレはこれまで買ってきたパンの重さが平均950gの正規分布となっていることを確認して、不正を見破ったそうです。

さて、ポアンカレのようにパン屋の不正を見破るには、一体どれくらいのパンを買う必要があるのでしょうか?
統計学の区間推定を用いると、以下の図のような定量的な解を与えることができます(Pythonで実装しました)。この場合ですと、おおよそパンを70個くらい買うと99.98%の確信度でパンが1000gとなるように作られていないことが分かります(かなり慎重)。

imagedraw (1).gif

統計学における四則演算

いきなり統計の区間推定に行く前に、統計学における四則演算の公式を理解する必要があります。公式の導出を行っていますが、面倒であれば結果とコメントだけを確認することである程度理解できると思います。

便宜上、以下のような記号と数式を定義します。また、簡単のためにパンの重量は整数値しか取らない離散値と仮定してしまいますが、連続値(実数)の場合は$\sum_{}$➡$\int$とするだけです。

記号と数式の定義
・$X=(x_1,x_2,x_3・・・x_n):$確率変数(パンの重さが取りえる値、例えば900g,901g,902g・・・1000g,1001g,1002gなど)

・$f(x):$確率関数(パンの重さがxとなる確率、例えばf(900g)=0.005, f(1000g)=0.1など)

・$E(X)=\sum_{k}x_{k}f(x_k)=μ_X:$確率変数$X$の期待値($\sum$パンの重さ×その重さのパンの出現確率)

・$V(X)=\sum_{k}(x_k-μ_X)^2f(x_k):$確率変数$X$の分散($\sum$(パンの重さ-全体のパンの重さの平均)$^2$×その重さのパンの出現確率)

確率変数の期待値と分散の四則演算を導出する上で、下記では互いに独立な確率変数$X$と$Y$を仮定することとします。XとYが互いに独立とは$f(x_i,y_j)=f(x_i)×f(y_j)$が成り立ち、$x_i$の発生確率と$y_i$の発生確率に関係性がないということです。例えば、ある日無造作に選んで買うパンの重みは互いに独立と考えることができます。

➀「確率変数$X$+確率変数$Y$」の期待値
確率変数$X$に確率変数$Y$を足した確率変数$X+Y$の期待値を求めていきます。

\begin{align}
E(X+Y)&=\sum_{j,k}(x_j+y_k)f(x_j,y_k)\\
&=\sum_{j,k}x_jf(x_j,y_k)+\sum_{j,k}y_kf(x_j,y_k)\\
&=\sum_{j}x_jf(x_j)\sum_{k}f(y_k)+\sum_{k}y_kf(y_k)\sum_{j}f(x_j)\\
&=\sum_{j}x_jf(x_j)+\sum_{k}y_kf(y_k)\\
&=E(X)+E(Y)
\end{align}

3行目の式は$X$と$Y$それぞれの周辺確率(それぞれ$X$と$Y$の影響を排除した確率)と呼ばれるものになっています。最終的に確率変数$X+Y$の期待値は、$X$と$Y$それぞれの期待値を足しただけのものとなりました。引き算では符号が変わるだけです。

➁「確率変数$X$×確率変数$Y$」の期待値
確率変数$X$と確率変数$Y$をかけあわせた確率変数$X×Y$の期待値を求めていきます。$\sum_{j,k}$の意味としては、取りえる全ての$j$と$k$において値を足し合わせる全探索のようなイメージが理解しやすいと思います。

\begin{align}
E(X×Y)=&\sum_{j,k}\bigl(x_jy_kf(x_j,y_k)\bigr)\\
=&x_1y_1f(x_1,y_1)+x_1y_2f(x_1,y_2)+・・・+x_1y_nf(x_1,y_n)\\
&+x_2y_1f(x_2,y_1)+x_2y_2f(x_2,y_2)・・・+x_2y_nf(x_2,y_n)\\
&・・・\\
&+x_my_1f(x_m,y_1)+x_my_2f(x_m,y_2)・・・+x_my_nf(x_m,y_n)\\
=&x_1f(x_1)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
&+x_2f(x_2)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
&・・・\\
&+x_mf(x_m)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
=&\sum_{j}x_jf(x_j)\sum_{k}y_kf(y_k)\\
=&E(X)E(Y)
\end{align}

確率変数$X×Y$の期待値は、確率変数$X$と$Y$の期待値をかけあわせただけのものになりました。割り算では$Y$の代わりに$1/Y$という確率変数を使えばよいです。

➂「確率変数$X$×定数$a$」の期待値
確率変数$X$に定数aをかけた確率変数$a×X$の期待値を求めていきます。

\begin{align}
E(a×X)&=\sum_{j}\bigl(a(x_j)f(x_j)\bigr)\\
&=ax_1f(x_1)+ax_2f(x_2)・・・\\
&=aE(X)
\end{align}

定数をかけただけの式となりました。割り算は定数$a$を$1/a$とするだけです。

➃「確率変数$X$+確率変数$Y$」の分散
確率変数$X$と確率変数$Y$を足した確率変数$X+Y$の分散を求めていきます。
便宜上、確率変数$X$の期待値は$E(X)=μ_{X}$として表します。

\begin{align}
V(X+Y)=&\sum_{j,k}\bigl(x_j+y_k-(μ_X+μ_Y)\bigr)^2f(x_j,y_k)\\
=&\sum_{j,k}\bigl(x_j^2+y_k^2+μ_X^2+μ_Y^2+2x_jy_k-2x_jμ_X-2x_jμ_Y-2y_kμ_X-2y_kμ_Y+2μ_Xμ_Y\bigr)f(x_j,y_k)\\
=&\sum_{j,k}\bigl(x_j-μ_X\bigr)^2f(x_j,y_k)+\sum_{j,k}\bigl(y_k-μ_Y\bigr)^2f(x_j,y_k)+2\sum_{j,k}\bigl(x_j-μ_X\bigr)\bigl(y_k-μ_Y\bigr)f(x_j,y_k)\\
=&\sum_{j}\bigl(x_j-μ_X\bigr)^2f(x_j)+\sum_{k}\bigl(y_k-μ_Y\bigr)^2f(y_k)+2\sum_{j}\bigl(x_j-μ_X\bigr)f(x_j)\sum_{k}\bigl(y_k-μ_Y\bigr)f(y_k)\\
=&V(X)+V(Y)
\end{align}

確率変数$X+Y$の分散は確率変数$X$と$Y$の分散を足し合わせただけの式となりました。ちなみに3行目の第3項目は$X$と$Y$の相関性を表す共分散という統計量で、$X$と$Y$が独立ではない場合は0とはならないので、分散に考慮する必要が出てきます。

➄「確率変数$X$×確率変数$Y$」の分散
確率変数$X$に確率変数$Y$をかけた確率変数$X$×$Y$の分散を求めていきます。

\begin{align}
V(X×Y)=&\sum_{j,k}\bigl(x_jy_k-μ_Xμ_Y\bigr)^2f(x_j,y_k)\\
=&\sum_{j,k}\bigl((x_jy_k)^2-2x_jy_kμ_Xμ_Y+(μ_Xμ_Y)^2\bigr)f(x_j,y_k)\\
=&\sum_{j,k}(x_jy_k)^2f(x_j,y_k)-μ_Xμ_Y\sum_{j,k}2x_jy_kf(x_j,y_k)+(μ_Xμ_Y)^2\\
=&\sum_{j,k}(x_jy_k)^2f(x_j,y_k)-2(μ_Xμ_Y)^2+(μ_Xμ_Y)^2\\
=&\sum_{j}x_j^2f(x_j)\sum_{k}y_k^2f(y_k)-(μ_Xμ_Y)^2 \\
=&E(X^2)E(Y^2)-(μ_Xμ_Y)^2\\
=&\bigl(V(X)+μ_X^2\bigr)\bigl(V(Y)+μ_Y^2\bigr)-(μ_Xμ_Y)^2 ※V(X)=\sum_{k}(x_k-μ_X)^2f(x_k)=E(X^2)-μ_X^2\\
=&V(X)V(Y)+μ_Y^2V(X)+μ_X^2V(Y)
\end{align}

確率変数$X×Y$の分散は、確率変数$X$と$Y$の期待値と分散を使って$V(X)V(Y)+μ_Y^2V(X)+μ_X^2V(Y)$となります。期待値のように単純に$V(X×Y)=V(X)V(Y)$とはならないので注意が必要です。

➅「確率変数$X$×定数$a$」の分散
確率変数$X$に定数aをかけた確率変数$a×X$の分散を求めていきます。

\begin{align}
V(a×X)=&\sum_{j}(ax_j-μ_{ax})^2f(x_j)\\
=&(ax_1-aμ_x)^2+(ax_2-aμ_x)^2+(ax_3-aμ_x)^2+・・・\\
=&a^2\sum_{j}(x_j-μ_{x})^2f(x_j)\\
=&a^2V(X)
\end{align}

確率変数を$a$倍すると分散は$a^2$倍となるので注意が必要です。

次回

次回は今回導出した統計の四則演算の公式を用いて、冒頭で示した図のように実際にパンの重みの分布を区間推定していくことに取り組みます。

参考

Pythonで理解する統計解析の基礎
Pythonで学ぶ統計検定2級の確率分布①
腑に落ちない人のための不偏性と一致性の解説
[統計学]t-分布、大数の法則、中心極限定理

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

【統計学を0から復習】推定統計学とは?

はじめに

統計学を復習しはじめたので、備忘録として自分なりの解釈をまとめていこうと思います。今回は統計学のもっとも基礎的な確率変数の四則演算についてまとめて、今後は推定・検定・多変量解析・ベイズ統計までまとめていこうと思います。統計を復習したいと思っている人や自分の統計に対する理解度を確認したい人の参考になるように心掛けます。

推定統計学とは

そもそも推定統計学とは何でしょうか。推定統計に関する有名な逸話としてポアンカレとパン屋の話があります。

その逸話とは以下のようなものです。
ポアンカレは馴染みのパン屋で、重量1000gのパンをよく買っていたそうです。ですが、どうも重さをごまかされていると感じたポアンカレは買ったパンの重さを毎回計ることにしました。
1年後にポアンカレはこれまで買ってきたパンの重さが平均950gの正規分布となっていることを確認して、不正を見破ったそうです。

さて、実際にポアンカレがパン屋の不正を見破るには、一体どれくらいのパンを買う必要があったのでしょうか?
統計学の区間推定を用いると、以下の図のような定量的な解を与えることができます(Pythonで実装しました)。この場合ですと、おおよそパンを70個くらい買うと99.98%の確信度でパン屋が不正していることが分かります(かなり慎重)。

imagedraw (1).gif

統計学における四則演算

いきなり統計の区間推定に行く前に、統計学における四則演算の公式を理解する必要があります。公式の導出を行っていますが、面倒であれば結果とコメントだけを確認することである程度理解できると思います。

便宜上、以下のような記号と数式を定義します。また、簡単のためにパンの重量は整数値しか取らない離散値と仮定してしまいますが、連続値(実数)の場合は$\sum_{}$➡$\int$とするだけです。

記号と数式の定義
・$X=(x_1,x_2,x_3・・・x_n):$確率変数(パンの重さが取りえる値、例えば900g,901g,902g・・・1000g,1001g,1002gなど)

・$f(x):$確率関数(パンの重さがxとなる確率、例えばf(900g)=0.005, f(1000g)=0.1など)

・$E(X)=\sum_{k}x_{k}f(x_k)=μ_X:$確率変数$X$の期待値($\sum$パンの重さ×その重さのパンの出現確率)

・$V(X)=\sum_{k}(x_k-μ_X)^2f(x_k):$確率変数$X$の分散($\sum$(パンの重さ-全体のパンの重さの平均)$^2$×その重さのパンの出現確率)

確率変数の期待値と分散の四則演算を導出する上で、下記では互いに独立な確率変数$X$と$Y$を仮定することとします。XとYが互いに独立とは$f(x_i,y_j)=f(x_i)×f(y_j)$が成り立ち、$x_i$の発生確率と$y_i$の発生確率に関係性がないということです。例えば、ある日無造作に選んで買うパンの重みは互いに独立と考えることができます。

➀「確率変数$X$+確率変数$Y$」の期待値
確率変数$X$に確率変数$Y$を足した確率変数$X+Y$の期待値を求めていきます。

\begin{align}
E(X+Y)&=\sum_{j,k}(x_j+y_k)f(x_j,y_k)\\
&=\sum_{j,k}x_jf(x_j,y_k)+\sum_{j,k}y_kf(x_j,y_k)\\
&=\sum_{j}x_jf(x_j)\sum_{k}f(y_k)+\sum_{k}y_kf(y_k)\sum_{j}f(x_j)\\
&=\sum_{j}x_jf(x_j)+\sum_{k}y_kf(y_k)\\
&=E(X)+E(Y)
\end{align}

3行目の式は$X$と$Y$それぞれの周辺確率(それぞれ$X$と$Y$の影響を排除した確率)と呼ばれるものになっています。最終的に確率変数$X+Y$の期待値は、$X$と$Y$それぞれの期待値を足しただけのものとなりました。引き算では符号が変わるだけです。

➁「確率変数$X$×確率変数$Y$」の期待値
確率変数$X$と確率変数$Y$をかけあわせた確率変数$X×Y$の期待値を求めていきます。$\sum_{j,k}$の意味としては、取りえる全ての$j$と$k$において値を足し合わせる全探索のようなイメージが理解しやすいと思います。

\begin{align}
E(X×Y)=&\sum_{j,k}\bigl(x_jy_kf(x_j,y_k)\bigr)\\
=&x_1y_1f(x_1,y_1)+x_1y_2f(x_1,y_2)+・・・+x_1y_nf(x_1,y_n)\\
&+x_2y_1f(x_2,y_1)+x_2y_2f(x_2,y_2)・・・+x_2y_nf(x_2,y_n)\\
&・・・\\
&+x_my_1f(x_m,y_1)+x_my_2f(x_m,y_2)・・・+x_my_nf(x_m,y_n)\\
=&x_1f(x_1)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
&+x_2f(x_2)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
&・・・\\
&+x_mf(x_m)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
=&\sum_{j}x_jf(x_j)\sum_{k}y_kf(y_k)\\
=&E(X)E(Y)
\end{align}

確率変数$X×Y$の期待値は、確率変数$X$と$Y$の期待値をかけあわせただけのものになりました。割り算では$Y$の代わりに$1/Y$という確率変数を使えばよいです。

➂「確率変数$X$×定数$a$」の期待値
確率変数$X$に定数aをかけた確率変数$a×X$の期待値を求めていきます。

\begin{align}
E(a×X)&=\sum_{j}\bigl(a(x_j)f(x_j)\bigr)\\
&=ax_1f(x_1)+ax_2f(x_2)・・・\\
&=aE(X)
\end{align}

定数をかけただけの式となりました。割り算は定数$a$を$1/a$とするだけです。

➃「確率変数$X$+確率変数$Y$」の分散
確率変数$X$と確率変数$Y$を足した確率変数$X+Y$の分散を求めていきます。
便宜上、確率変数$X$の期待値は$E(X)=μ_{X}$として表します。

\begin{align}
V(X+Y)=&\sum_{j,k}\bigl(x_j+y_k-(μ_X+μ_Y)\bigr)^2f(x_j,y_k)\\
=&\sum_{j,k}\bigl(x_j^2+y_k^2+μ_X^2+μ_Y^2+2x_jy_k-2x_jμ_X-2x_jμ_Y-2y_kμ_X-2y_kμ_Y+2μ_Xμ_Y\bigr)f(x_j,y_k)\\
=&\sum_{j,k}\bigl(x_j-μ_X\bigr)^2f(x_j,y_k)+\sum_{j,k}\bigl(y_k-μ_Y\bigr)^2f(x_j,y_k)+2\sum_{j,k}\bigl(x_j-μ_X\bigr)\bigl(y_k-μ_Y\bigr)f(x_j,y_k)\\
=&\sum_{j}\bigl(x_j-μ_X\bigr)^2f(x_j)+\sum_{k}\bigl(y_k-μ_Y\bigr)^2f(y_k)+2\sum_{j}\bigl(x_j-μ_X\bigr)f(x_j)\sum_{k}\bigl(y_k-μ_Y\bigr)f(y_k)\\
=&V(X)+V(Y)
\end{align}

確率変数$X+Y$の分散は確率変数$X$と$Y$の分散を足し合わせただけの式となりました。ちなみに3行目の第3項目は$X$と$Y$の相関性を表す共分散という統計量で、$X$と$Y$が独立ではない場合は0とはならないので、分散に考慮する必要が出てきます。

➄「確率変数$X$×確率変数$Y$」の分散
確率変数$X$に確率変数$Y$をかけた確率変数$X$×$Y$の分散を求めていきます。

\begin{align}
V(X×Y)=&\sum_{j,k}\bigl(x_jy_k-μ_Xμ_Y\bigr)^2f(x_j,y_k)\\
=&\sum_{j,k}\bigl((x_jy_k)^2-2x_jy_kμ_Xμ_Y+(μ_Xμ_Y)^2\bigr)f(x_j,y_k)\\
=&\sum_{j,k}(x_jy_k)^2f(x_j,y_k)-μ_Xμ_Y\sum_{j,k}2x_jy_kf(x_j,y_k)+(μ_Xμ_Y)^2\\
=&\sum_{j,k}(x_jy_k)^2f(x_j,y_k)-2(μ_Xμ_Y)^2+(μ_Xμ_Y)^2\\
=&\sum_{j}x_j^2f(x_j)\sum_{k}y_k^2f(y_k)-(μ_Xμ_Y)^2 \\
=&E(X^2)E(Y^2)-(μ_Xμ_Y)^2\\
=&\bigl(V(X)+μ_X^2\bigr)\bigl(V(Y)+μ_Y^2\bigr)-(μ_Xμ_Y)^2 ※V(X)=\sum_{k}(x_k-μ_X)^2f(x_k)=E(X^2)-μ_X^2\\
=&V(X)V(Y)+μ_Y^2V(X)+μ_X^2V(Y)
\end{align}

確率変数$X×Y$の分散は、確率変数$X$と$Y$の期待値と分散を使って$V(X)V(Y)+μ_Y^2V(X)+μ_X^2V(Y)$となります。期待値のように単純に$V(X×Y)=V(X)V(Y)$とはならないので注意が必要です。

➅「確率変数$X$×定数$a$」の分散
確率変数$X$に定数aをかけた確率変数$a×X$の分散を求めていきます。

\begin{align}
V(a×X)=&\sum_{j}(ax_j-μ_{ax})^2f(x_j)\\
=&(ax_1-aμ_x)^2+(ax_2-aμ_x)^2+(ax_3-aμ_x)^2+・・・\\
=&a^2\sum_{j}(x_j-μ_{x})^2f(x_j)\\
=&a^2V(X)
\end{align}

確率変数を$a$倍すると分散は$a^2$倍となるので注意が必要です。

次回

次回は今回導出した統計の四則演算の公式を用いて、冒頭で示した図のように実際にパンの重みの分布を区間推定していくことに取り組みます。

参考

Pythonで理解する統計解析の基礎
Pythonで学ぶ統計検定2級の確率分布①
腑に落ちない人のための不偏性と一致性の解説
[統計学]t-分布、大数の法則、中心極限定理

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

推定統計学とは?&確率変数の四則演算

はじめに

統計学を復習しはじめたので、備忘録として自分なりの解釈をまとめていこうと思います。今回は統計学のもっとも基礎的な確率変数の四則演算についてまとめて、今後は推定・検定・多変量解析・ベイズ統計までまとめていこうと思います。統計を復習したいと思っている人や自分の統計に対する理解度を確認したい人の参考になるように心掛けます。

推定統計学とは

そもそも推定統計学とは何でしょうか。推定統計に関する有名な逸話としてポアンカレとパン屋の話があります。

その逸話とは以下のようなものです。
ポアンカレは馴染みのパン屋で、重量1000gのパンをよく買っていたそうです。ですが、どうも重さをごまかされていると感じたポアンカレは買ったパンの重さを毎回計ることにしました。
1年後にポアンカレはこれまで買ってきたパンの重さが平均950gの正規分布となっていることを確認して、不正を見破ったそうです。

さて、実際にポアンカレがパン屋の不正を見破るには、一体どれくらいのパンを買う必要があったのでしょうか?
統計学の区間推定を用いると、以下の図のような定量的な解を与えることができます(Pythonで実装しました)。この場合ですと、おおよそパンを70個くらい買うと99.98%の確信度でパン屋が不正していることが分かります(かなり慎重)。

imagedraw (1).gif

統計学における四則演算

いきなり統計の区間推定に行く前に、統計学における四則演算の公式を理解する必要があります。公式の導出を行っていますが、面倒であれば結果とコメントだけを確認することである程度理解できると思います。

便宜上、以下のような記号と数式を定義します。また、簡単のためにパンの重量は整数値しか取らない離散値と仮定してしまいますが、連続値(実数)の場合は$\sum_{}$➡$\int$とするだけです。

記号と数式の定義
・$X=(x_1,x_2,x_3・・・x_n):$確率変数(パンの重さが取りえる値、例えば900g,901g,902g・・・1000g,1001g,1002gなど)

・$f(x):$確率関数(パンの重さがxとなる確率、例えばf(900g)=0.005, f(1000g)=0.1など)

・$E(X)=\sum_{k}x_{k}f(x_k)=μ_X:$確率変数$X$の期待値($\sum$パンの重さ×その重さのパンの出現確率)

・$V(X)=\sum_{k}(x_k-μ_X)^2f(x_k):$確率変数$X$の分散($\sum$(パンの重さ-全体のパンの重さの平均)$^2$×その重さのパンの出現確率)

確率変数の期待値と分散の四則演算を導出する上で、下記では互いに独立な確率変数$X$と$Y$を仮定することとします。XとYが互いに独立とは$f(x_i,y_j)=f(x_i)×f(y_j)$が成り立ち、$x_i$の発生確率と$y_i$の発生確率に関係性がないということです。例えば、ある日無造作に選んで買うパンの重みは互いに独立と考えることができます。

➀「確率変数$X$+確率変数$Y$」の期待値
確率変数$X$に確率変数$Y$を足した確率変数$X+Y$の期待値を求めていきます。

\begin{align}
E(X+Y)&=\sum_{j,k}(x_j+y_k)f(x_j,y_k)\\
&=\sum_{j,k}x_jf(x_j,y_k)+\sum_{j,k}y_kf(x_j,y_k)\\
&=\sum_{j}x_jf(x_j)\sum_{k}f(y_k)+\sum_{k}y_kf(y_k)\sum_{j}f(x_j)\\
&=\sum_{j}x_jf(x_j)+\sum_{k}y_kf(y_k)\\
&=E(X)+E(Y)
\end{align}

3行目の式は$X$と$Y$それぞれの周辺確率(それぞれ$X$と$Y$の影響を排除した確率)と呼ばれるものになっています。最終的に確率変数$X+Y$の期待値は、$X$と$Y$それぞれの期待値を足しただけのものとなりました。引き算では符号が変わるだけです。

➁「確率変数$X$×確率変数$Y$」の期待値
確率変数$X$と確率変数$Y$をかけあわせた確率変数$X×Y$の期待値を求めていきます。$\sum_{j,k}$の意味としては、取りえる全ての$j$と$k$において値を足し合わせる全探索のようなイメージが理解しやすいと思います。

\begin{align}
E(X×Y)=&\sum_{j,k}\bigl(x_jy_kf(x_j,y_k)\bigr)\\
=&x_1y_1f(x_1,y_1)+x_1y_2f(x_1,y_2)+・・・+x_1y_nf(x_1,y_n)\\
&+x_2y_1f(x_2,y_1)+x_2y_2f(x_2,y_2)・・・+x_2y_nf(x_2,y_n)\\
&・・・\\
&+x_my_1f(x_m,y_1)+x_my_2f(x_m,y_2)・・・+x_my_nf(x_m,y_n)\\
=&x_1f(x_1)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
&+x_2f(x_2)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
&・・・\\
&+x_mf(x_m)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
=&\sum_{j}x_jf(x_j)\sum_{k}y_kf(y_k)\\
=&E(X)E(Y)
\end{align}

確率変数$X×Y$の期待値は、確率変数$X$と$Y$の期待値をかけあわせただけのものになりました。割り算では$Y$の代わりに$1/Y$という確率変数を使えばよいです。

➂「確率変数$X$×定数$a$」の期待値
確率変数$X$に定数aをかけた確率変数$a×X$の期待値を求めていきます。

\begin{align}
E(a×X)&=\sum_{j}\bigl(a(x_j)f(x_j)\bigr)\\
&=ax_1f(x_1)+ax_2f(x_2)・・・\\
&=aE(X)
\end{align}

定数をかけただけの式となりました。割り算は定数$a$を$1/a$とするだけです。

➃「確率変数$X$+確率変数$Y$」の分散
確率変数$X$と確率変数$Y$を足した確率変数$X+Y$の分散を求めていきます。
便宜上、確率変数$X$の期待値は$E(X)=μ_{X}$として表します。

\begin{align}
V(X+Y)=&\sum_{j,k}\bigl(x_j+y_k-(μ_X+μ_Y)\bigr)^2f(x_j,y_k)\\
=&\sum_{j,k}\bigl(x_j^2+y_k^2+μ_X^2+μ_Y^2+2x_jy_k-2x_jμ_X-2x_jμ_Y-2y_kμ_X-2y_kμ_Y+2μ_Xμ_Y\bigr)f(x_j,y_k)\\
=&\sum_{j,k}\bigl(x_j-μ_X\bigr)^2f(x_j,y_k)+\sum_{j,k}\bigl(y_k-μ_Y\bigr)^2f(x_j,y_k)+2\sum_{j,k}\bigl(x_j-μ_X\bigr)\bigl(y_k-μ_Y\bigr)f(x_j,y_k)\\
=&\sum_{j}\bigl(x_j-μ_X\bigr)^2f(x_j)+\sum_{k}\bigl(y_k-μ_Y\bigr)^2f(y_k)+2\sum_{j}\bigl(x_j-μ_X\bigr)f(x_j)\sum_{k}\bigl(y_k-μ_Y\bigr)f(y_k)\\
=&V(X)+V(Y)
\end{align}

確率変数$X+Y$の分散は確率変数$X$と$Y$の分散を足し合わせただけの式となりました。ちなみに3行目の第3項目は$X$と$Y$の相関性を表す共分散という統計量で、$X$と$Y$が独立ではない場合は0とはならないので、分散に考慮する必要が出てきます。

➄「確率変数$X$×確率変数$Y$」の分散
確率変数$X$に確率変数$Y$をかけた確率変数$X$×$Y$の分散を求めていきます。

\begin{align}
V(X×Y)=&\sum_{j,k}\bigl(x_jy_k-μ_Xμ_Y\bigr)^2f(x_j,y_k)\\
=&\sum_{j,k}\bigl((x_jy_k)^2-2x_jy_kμ_Xμ_Y+(μ_Xμ_Y)^2\bigr)f(x_j,y_k)\\
=&\sum_{j,k}(x_jy_k)^2f(x_j,y_k)-μ_Xμ_Y\sum_{j,k}2x_jy_kf(x_j,y_k)+(μ_Xμ_Y)^2\\
=&\sum_{j,k}(x_jy_k)^2f(x_j,y_k)-2(μ_Xμ_Y)^2+(μ_Xμ_Y)^2\\
=&\sum_{j}x_j^2f(x_j)\sum_{k}y_k^2f(y_k)-(μ_Xμ_Y)^2 \\
=&E(X^2)E(Y^2)-(μ_Xμ_Y)^2\\
=&\bigl(V(X)+μ_X^2\bigr)\bigl(V(Y)+μ_Y^2\bigr)-(μ_Xμ_Y)^2 ※V(X)=\sum_{k}(x_k-μ_X)^2f(x_k)=E(X^2)-μ_X^2\\
=&V(X)V(Y)+μ_Y^2V(X)+μ_X^2V(Y)
\end{align}

確率変数$X×Y$の分散は、確率変数$X$と$Y$の期待値と分散を使って$V(X)V(Y)+μ_Y^2V(X)+μ_X^2V(Y)$となります。期待値のように単純に$V(X×Y)=V(X)V(Y)$とはならないので注意が必要です。

➅「確率変数$X$×定数$a$」の分散
確率変数$X$に定数aをかけた確率変数$a×X$の分散を求めていきます。

\begin{align}
V(a×X)=&\sum_{j}(ax_j-μ_{ax})^2f(x_j)\\
=&(ax_1-aμ_x)^2+(ax_2-aμ_x)^2+(ax_3-aμ_x)^2+・・・\\
=&a^2\sum_{j}(x_j-μ_{x})^2f(x_j)\\
=&a^2V(X)
\end{align}

確率変数を$a$倍すると分散は$a^2$倍となるので注意が必要です。

次回

次回は今回導出した統計の四則演算の公式を用いて、冒頭で示した図のように実際にパンの重みの分布を区間推定していくことに取り組みます。

参考

Pythonで理解する統計解析の基礎
Pythonで学ぶ統計検定2級の確率分布①
腑に落ちない人のための不偏性と一致性の解説
[統計学]t-分布、大数の法則、中心極限定理

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

【統計学を0から復習】確率変数の四則演算

はじめに

統計学を復習しはじめたので、備忘録として自分なりの解釈をまとめていこうと思います。今回は統計学のもっとも基礎的な確率変数の四則演算についてまとめて、今後は推定・検定・多変量解析・ベイズ統計までまとめていこうと思います。統計を復習したいと思っている人や自分の統計に対する理解度を確認したい人の参考になるように心掛けます。

推定統計学とは

そもそも推定統計学とは何でしょうか。推定統計に関する有名な逸話としてポアンカレとパン屋の話があります。

その逸話とは以下のようなものです。
ポアンカレは馴染みのパン屋で、重量1000gのパンをよく買っていたそうです。ですが、どうも重さをごまかされていると感じたポアンカレは買ったパンの重さを毎回計ることにしました。
1年後にポアンカレはこれまで買ってきたパンの重さが平均950gの正規分布となっていることを確認して、不正を見破ったそうです。

さて、実際にポアンカレがパン屋の不正を見破るには、一体どれくらいのパンを買う必要があったのでしょうか?
統計学の区間推定を用いると、以下の図のような定量的な解を得ることができます(Pythonで実装しました)。この場合ですと、おおよそパンを70個くらい買うと99.98%の確信度でパン屋が不正していることが分かります(かなり慎重)。

imagedraw (1).gif

統計学における四則演算

いきなり統計の区間推定に行く前に、統計学における四則演算の公式を理解する必要があります。公式の導出を行っていますが、面倒であれば結果とコメントだけを確認することである程度理解できると思います。

便宜上、以下のような記号と数式を定義します。また、簡単のためにパンの重量は整数値しか取らない離散値と仮定してしまいますが、連続値(実数)の場合は$\sum_{}$を$\int$とするだけです。

記号と数式の定義
・$X=(x_1,x_2,x_3・・・x_n):$確率変数(パンの重さが取りえる値、例えば900g,901g,902g・・・1000g,1001g,1002gなど)

・$f(x):$確率関数(パンの重さがxとなる確率、例えばf(900g)=0.005, f(1000g)=0.1など)

・$E(X)=\sum_{k}x_{k}f(x_k)=μ_X:$確率変数$X$の期待値($\sum$パンの重さ×その重さのパンの出現確率)

・$V(X)=\sum_{k}(x_k-μ_X)^2f(x_k):$確率変数$X$の分散($\sum$(パンの重さ-全体のパンの重さの平均)$^2$×その重さのパンの出現確率)

確率変数の期待値と分散の四則演算を導出する上で、下記では互いに独立な確率変数$X$と$Y$を仮定することとします。XとYが互いに独立とは$f(x_i,y_j)=f(x_i)×f(y_j)$が成り立ち、$x_i$の発生確率と$y_i$の発生確率に関係性がないということです。例えば、ある日無造作に選んで買うパンの重みは互いに独立と考えることができます。

➀「確率変数$X$+確率変数$Y$」の期待値
確率変数$X$に確率変数$Y$を足した確率変数$X+Y$の期待値を求めていきます。

\begin{align}
E(X+Y)&=\sum_{j,k}(x_j+y_k)f(x_j,y_k)\\
&=\sum_{j,k}x_jf(x_j,y_k)+\sum_{j,k}y_kf(x_j,y_k)\\
&=\sum_{j}x_jf(x_j)\sum_{k}f(y_k)+\sum_{k}y_kf(y_k)\sum_{j}f(x_j)\\
&=\sum_{j}x_jf(x_j)+\sum_{k}y_kf(y_k)\\
&=E(X)+E(Y)
\end{align}

3行目の式は$X$と$Y$それぞれの周辺確率(それぞれ$X$と$Y$の影響を排除した確率)と呼ばれるものになっています。最終的に確率変数$X+Y$の期待値は、$X$と$Y$それぞれの期待値を足しただけのものとなりました。引き算では符号が変わるだけです。

➁「確率変数$X$×確率変数$Y$」の期待値
確率変数$X$と確率変数$Y$をかけあわせた確率変数$X×Y$の期待値を求めていきます。$\sum_{j,k}$の意味としては、取りえる全ての$j$と$k$において値を足し合わせる全探索のようなイメージが理解しやすいと思います。

\begin{align}
E(X×Y)=&\sum_{j,k}\bigl(x_jy_kf(x_j,y_k)\bigr)\\
=&x_1y_1f(x_1,y_1)+x_1y_2f(x_1,y_2)+・・・+x_1y_nf(x_1,y_n)\\
&+x_2y_1f(x_2,y_1)+x_2y_2f(x_2,y_2)・・・+x_2y_nf(x_2,y_n)\\
&・・・\\
&+x_my_1f(x_m,y_1)+x_my_2f(x_m,y_2)・・・+x_my_nf(x_m,y_n)\\
=&x_1f(x_1)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
&+x_2f(x_2)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
&・・・\\
&+x_mf(x_m)\bigl(y_1f(y_1)+y_2f(y_2)+・・・y_nf(y_n)\bigr)\\
=&\sum_{j}x_jf(x_j)\sum_{k}y_kf(y_k)\\
=&E(X)E(Y)
\end{align}

確率変数$X×Y$の期待値は、確率変数$X$と$Y$の期待値をかけあわせただけのものになりました。割り算では$Y$の代わりに$1/Y$という確率変数を使えばよいです。

➂「確率変数$X$×定数$a$」の期待値
確率変数$X$に定数aをかけた確率変数$a×X$の期待値を求めていきます。

\begin{align}
E(a×X)&=\sum_{j}\bigl(a(x_j)f(x_j)\bigr)\\
&=ax_1f(x_1)+ax_2f(x_2)・・・\\
&=aE(X)
\end{align}

定数をかけただけの式となりました。割り算は定数$a$を$1/a$とするだけです。

➃「確率変数$X$+確率変数$Y$」の分散
確率変数$X$と確率変数$Y$を足した確率変数$X+Y$の分散を求めていきます。
便宜上、確率変数$X$の期待値は$E(X)=μ_{X}$として表します。

\begin{align}
V(X+Y)=&\sum_{j,k}\bigl(x_j+y_k-(μ_X+μ_Y)\bigr)^2f(x_j,y_k)\\
=&\sum_{j,k}\bigl(x_j^2+y_k^2+μ_X^2+μ_Y^2+2x_jy_k-2x_jμ_X-2x_jμ_Y-2y_kμ_X-2y_kμ_Y+2μ_Xμ_Y\bigr)f(x_j,y_k)\\
=&\sum_{j,k}\bigl(x_j-μ_X\bigr)^2f(x_j,y_k)+\sum_{j,k}\bigl(y_k-μ_Y\bigr)^2f(x_j,y_k)+2\sum_{j,k}\bigl(x_j-μ_X\bigr)\bigl(y_k-μ_Y\bigr)f(x_j,y_k)\\
=&\sum_{j}\bigl(x_j-μ_X\bigr)^2f(x_j)+\sum_{k}\bigl(y_k-μ_Y\bigr)^2f(y_k)+2\sum_{j}\bigl(x_j-μ_X\bigr)f(x_j)\sum_{k}\bigl(y_k-μ_Y\bigr)f(y_k)\\
=&V(X)+V(Y)
\end{align}

確率変数$X+Y$の分散は確率変数$X$と$Y$の分散を足し合わせただけの式となりました。ちなみに3行目の第3項目は$X$と$Y$の相関性を表す共分散という統計量で、$X$と$Y$が独立ではない場合は0とはならないので、分散に考慮する必要が出てきます。

➄「確率変数$X$×確率変数$Y$」の分散
確率変数$X$に確率変数$Y$をかけた確率変数$X$×$Y$の分散を求めていきます。

\begin{align}
V(X×Y)=&\sum_{j,k}\bigl(x_jy_k-μ_Xμ_Y\bigr)^2f(x_j,y_k)\\
=&\sum_{j,k}\bigl((x_jy_k)^2-2x_jy_kμ_Xμ_Y+(μ_Xμ_Y)^2\bigr)f(x_j,y_k)\\
=&\sum_{j,k}(x_jy_k)^2f(x_j,y_k)-μ_Xμ_Y\sum_{j,k}2x_jy_kf(x_j,y_k)+(μ_Xμ_Y)^2\\
=&\sum_{j,k}(x_jy_k)^2f(x_j,y_k)-2(μ_Xμ_Y)^2+(μ_Xμ_Y)^2\\
=&\sum_{j}x_j^2f(x_j)\sum_{k}y_k^2f(y_k)-(μ_Xμ_Y)^2 \\
=&E(X^2)E(Y^2)-(μ_Xμ_Y)^2\\
=&\bigl(V(X)+μ_X^2\bigr)\bigl(V(Y)+μ_Y^2\bigr)-(μ_Xμ_Y)^2 ※V(X)=\sum_{k}(x_k-μ_X)^2f(x_k)=E(X^2)-μ_X^2\\
=&V(X)V(Y)+μ_Y^2V(X)+μ_X^2V(Y)
\end{align}

確率変数$X×Y$の分散は、確率変数$X$と$Y$の期待値と分散を使って$V(X)V(Y)+μ_Y^2V(X)+μ_X^2V(Y)$となります。期待値のように単純に$V(X×Y)=V(X)V(Y)$とはならないので注意が必要です。

➅「確率変数$X$×定数$a$」の分散
確率変数$X$に定数aをかけた確率変数$a×X$の分散を求めていきます。

\begin{align}
V(a×X)=&\sum_{j}(ax_j-μ_{ax})^2f(x_j)\\
=&(ax_1-aμ_x)^2+(ax_2-aμ_x)^2+(ax_3-aμ_x)^2+・・・\\
=&a^2\sum_{j}(x_j-μ_{x})^2f(x_j)\\
=&a^2V(X)
\end{align}

確率変数を$a$倍すると分散は$a^2$倍となるので注意が必要です。

次回

次回は今回導出した統計の四則演算の公式を用いて、冒頭で示した図のように実際にパンの重みの分布を区間推定していくことに取り組みます。

参考

Pythonで理解する統計解析の基礎
Pythonで学ぶ統計検定2級の確率分布①
腑に落ちない人のための不偏性と一致性の解説
[統計学]t-分布、大数の法則、中心極限定理

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

【python】【scikit-learn】k-最近傍法入門メモ

Scikit-learnの学習メモ

scikit-learnの命名規則

文字 意味
X データ
y ラベル

train_test_split関数

データ(X, y)をトレーニング用・評価用に分割する関数

分割を行う前に擬似乱数を用いてデータセットをシャッフルする。

データポイントはラベルでソートされているので、最後の25%をテストセットにすると,全てのデータポイントがラベル2(1つの値)になってしまうような事態を避けるため。

train_test_split関数でデータ分割
乱数のシード

jupyter_notebook.ipynb
train_test_split( 第一引数: 特徴行列X, 第二引数: 目的変数y, test_size(=0.3): テスト用のデータの大きさの割合, random_state= : データを分割する際の乱数のシード値)

random_state=0とすると、出力が決定的になり、常に同じ結果が得られるようになる。(勉強用)

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
    iris_dataset['data'], iris_dataset['target'], random_state=0)

pandas.DataFrame

pandas.DataFrame

pandas.DataFrame
import pandas as pd

#リファレンス
pandas.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)

#例
iris_dataframe = pd.DataFrame(X_train, columns=iris_dataset.feature_names)

出力結果
スクリーンショット 2020-08-06 15.17.20.png

pandas.plotting.scatter_matrix

pandas.plotting.scatter_matrix

pandas.plotting.scatter_matrix
#公式リファレンス
pandas.plotting.scatter_matrix(frame, alpha=0.5, figsize=None, ax=None, grid=False, diagonal='hist', marker='.', density_kwds=None, hist_kwds=None, range_padding=0.05, **kwargs)

#iris例
iris_dataframe = pd.DataFrame(X_train, columns=iris_dataset.feature_names)
grr = pd.plotting.scatter_matrix(iris_dataframe, c=y_train, figsize=(8, 8), marker='o',hist_kwds={'bins' : 20}, s=60, alpha=.8)

出力結果
スクリーンショット 2020-08-06 15.26.52.png

scikit-learnは常に2次元配列

scikit-learn
X_new = np.array([[5, 2.9, 1, 0.2]])

sklearn.neighbors.KNeighborsClassifier

k-最近傍法によるクラス分類

sklearn.neighbors.KNeighborsClassifier

neighbors.KNeighborsClassifier
#大切なメソッド

.fit(X, y)
#Xを学習データ、yを目標値としてモデルを適合させる

.predict(X)
#提供されたデータのクラスラベルを予測します。

.score(X, y)
#与えられたテストデータとラベルの平均精度を返します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

物体検出のYOLOv5やーる

はじめに

物体検出のYOLOv5をやってみます。

開発環境

  • Windows10
  • Python 3.6
  • PyTorch 1.5.1

導入

1.ここからクローンします。

2.YOLOv5環境を作成します。

conda create -n yolov5 python=3.6
conda activate yolov5
cd yolov5

3.各ライブラリをインストールします。

先にPyTorchをインストールします。

pip install torch===1.5.1 torchvision===0.6.1 -f https://download.pytorch.org/whl/torch_stable.html

次にrequirements.txtからライブラリをインストールします。

pip install -U -r requirements.txt

実行

下記コマンドを実行し、ウェブカメラの映像を入力します。

python detect.py --source 0

SnapCameraなどにウェブカメラを占有される場合は、utils/datasets.pyのLoadStreamsクラス237行目の

cap = cv2.VideoCapture(0 if s == '0' else s)

の0のところを1などに変えてみてください。

cap = cv2.VideoCapture(1 if s == '0' else s)

ここから他のモデルをダウンロードし、下記の--weightsオプションでパスを指定し実行することも可能です。

python detect.py --source 0 --weights yolov5x.pt

各モデルの比較はこちらの記事YOLO V5(V3との比較有)を簡単サクッと試す!が参考になります。

スノボーとスケボー、認識できていてすごい!!

お疲れ様でした。

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

【RaspberryPi】PythonのデフォルトをPython3に変更

はじめに

検証のため、会社でRaspberryPi zeroを購入し、セットアップしていました。
作成してあったPythonスクリプトを実行すると挙動がおかしい。

確認せず実行している私も悪いですが、実行バージョンが2.7じゃないですか。
3.7が入っていることを確認していたので、そいつを実行していると思い込んでいました。

備忘録として変更方法を記事にします。

デフォルトをPython3に!

バージョン確認

python --version

ここで3系なら終わりです。私は2系なので続けます。

シンボリックリンク確認

ls -l /usr/bin/py*

2.png

pythonはpython2.7を使っていることがわかります。
リンクをPython3に変えてあげれば解決!!!

シンボリックリンク解除

unlink python

これでまずpythonが参照していたpython2へのリンクが解除。

シンボリックリンク設定

ln -s python3 python

python3を参照するpythonのシンボリックリンクを作成。3.png
ちゃんとリンクができていることが確認できます。

再びPythonのバージョンを確認して変更できていれば成功!!!
(今回の場合、Python3.7.3になっています)

最後に

こういう細かいこと(私的に)はすぐに忘れてしまう特性があります。
特に環境設定でどうやるんだっけか?となって、何度も同じことを調べている自分が。。。

一人でやっていると疎かになりがちなので、気を付けようと思う今日この頃。。。

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

インスタンスセグメンテーションのYOLACTやーる(Windows10、Python3.7、CUDA10.0、Pytorch 1.2.0)

はじめに

リアルタイムインスタンスセグメンテーションのYOLACTをやってみます。

開発環境

  • Windows 10
  • Python 3.7
  • CUDA 10.0
  • PyTorch 1.2.0

導入

1.ここからクローンします。

2.YOLACT環境を作成します。

conda create -n yolact python=3.7
conda activate yolact
cd yolact-master

3.各ライブラリをインストールします。
requirements.txtを作成し、インストールします。

requirements.txt
cython 
# pytorch::torchvision
# pytorch::pytorch >=1.0.1
# cudatoolkit
# cudnn
# pytorch::cuda100
matplotlib
# git # to download COCO dataset
# curl # to download COCO dataset
# unzip # to download COCO dataset
# conda-forge::bash # to download COCO dataset
opencv-python 
pillow<7.0 # bug PILLOW_VERSION in torchvision, must be < 7.0 until torchvision is upgraded
# pycocotools 
PyQt5 # needed on KDE/Qt envs for matplotlib
pip install -r requirements.txt 

pycocotoolsをインストールします。I can't install cocoapi on Windows 10 #185

pip install "git+https://github.com/philferriere/cocoapi.git#egg=pycocotools&subdirectory=PythonAPI"

PyTorchをインストールします。PyTorch v1.2.0 CUDA 10.0

pip install torch==1.2.0 torchvision==0.4.0 -f https://download.pytorch.org/whl/torch_stable.html

4.weightsフォルダを作成し、yolact_base_54_800000.pthをダウンロードして、置きます。

実行

下記コマンドを実行してみましょう。input_image.pngを変更して、任意の画像を指定してください。

python eval.py --trained_model=weights/yolact_base_54_800000.pth --score_threshold=0.15 --top_k=15 --image=input_image.png:output_image.png
input output
outside_000001.jpg output_image.png

下記コマンドを実行し、ウェブカメラの映像を入力します。

python eval.py --trained_model=weights/yolact_base_54_800000.pth --score_threshold=0.15 --top_k=15 --video_multiframe=4 --video=0

SnapCamera等にウェブカメラを占有されている場合は、--video=1などに変更してみてください。
処理速度は8.3fpsくらいでした。

YOLACT++

YOLACT++を実行するためには、DCNv2をセットアップする必要がありますが、

cd external/DCNv2
python setup.py build develop

Visual Studioのエラーが出ました。

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\include\crt/host_config.h(133): fatal error C1189: #error:  -- unsupported Microsoft Visual Studio version! Only the versions 2012, 2013, 2015 and 2017 are supported!
error: command 'C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v9.0\\bin\\nvcc.exe' failed with exit status 2

エラーの内容を見ると、CUDA9.0のパスが読まれており、しかもVisual Studio 2019には対応していないようです。CUDAのパス(システム環境変数)を10.0にしてみましたが、Visual Studio 2017が入っているのにもかかわらず、参照してくれない模様。Visual Studio 2019はアンインストールしたくないのであきらめることにします。

お疲れ様でした。

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