AB型技術系 主に備忘録

ほぼプログラム関連の備忘録

NVIDIA GeForce GT 710でStable Diffusion WebUI

生成AIを試したくてStable Diffusion WebUIをインストールした時のメモ書きです。

NVIDIA GeForce GT 710で試す

freelancer.hatenablog.jp

NVIDIA GeForce GT 710で試してみましたが、当然スペック不足。
12GB以上のVRAMを搭載したグラボを購入することにしたのが前回のお話。

10万円以下、VRAM12GB以上のグラボ

  • GeForce RTX 4070 VENTUS 12GB
  • GeForce RTX 4060 Ti VENTUS 16GB
  • GeForce RTX 3060 VENTUS 12G

コスパ重視の3060(40,000円)、性能重視の4070(85,000円)、その間の4060 Ti(75,000円)

Stable Diffusionを使ったベンチマークを見ると3060で十分と判断して3060 を購入。

PCスペック

  • OS:Windows10
  • CPU:Ryzen 9 3900X
  • GPU:GeForce RTX 3060 VENTUS 2X 12G OC
  • VRAM:12GB
  • メモリ:32GB

Stable Diffusion WebUIを起動する

Stable Diffusion WebUI
Stable Diffusion WebUI

起動時にエラーがでなくなりました。

http://127.0.0.1:7860

再度"dog"と入力して生成してみました。

Stable Diffusion WebUI

OutOfMemoryErrorは発生せず。
犬の画像が生成されました。

Stable Diffusion WebUI
dog
Steps: 20, Sampler: DPM++ 2M Karras, CFG scale: 7, Seed: 751866062, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, VAE hash: 735e4c3a44, VAE: vae-ft-mse-840000-ema-pruned.safetensors, Clip skip: 2, Version: 1.6.1

Time taken: 3.7 sec.

A: 3.09 GB, R: 3.60 GB, Sys: 5.6/12 GB (47.1%)

結論

Stable Diffusionを使うならミドルクラスのグラボは必須。



NVIDIA GeForce GT 710でStable Diffusion WebUIは当然動かない

生成AIを試したくてStable Diffusion WebUIをインストールした時のメモ書きです。

Stable Diffusion WebUI

stablediffusionweb.com

PCスペック

  • OS:Windows10
  • CPU:Ryzen 9 3900X
  • GPU:玄人志向 NVIDIA GeForce GT 710
  • VRAM:1GB
  • メモリ:32GB

結論を先に言うとVRAMが圧倒的に足りなくて動きませんでした。
12GB以上推奨らしいです。

Pythonのインストール

Stable Diffusionを動かすためにPythonが必要なのでインストールします。

www.python.org

Python3.10.11
Python3.10.11
Python3.10.11

gitのインストール

githubからStable Diffusion WebUIをダウンロードするためにgitをインストールします。

gitforwindows.org

git2.42.0.2
git2.42.0.2

TortoiseGitのインストール

gitクライアントは仕事で使い慣れているTortoiseGitにしました。

TortoiseGit2.15.0

Stable Diffusion WebUIのダウンロード

github.com

Stable Diffusion WebUI

Stable Diffusion WebUIを起動する

ダウンロードしたフォルダにあるwebui.batを起動する

Stable Diffusion WebUI
Stable Diffusion WebUI

OutOfMemoryErrorが発生しましたが、画面自体は表示できました。

http://127.0.0.1:7860

Stable Diffusion WebUI

試しに"dog"と入力して生成してみました。

Stable Diffusion WebUI

OutOfMemoryErrorが発生、12GB推奨で1GBでは無理ですよね。

Stable Diffusion WebUI
OutOfMemoryError: CUDA out of memory. 
Tried to allocate 50.00 MiB 
(GPU 0; 1024.00 MiB total capacity; 627.97 MiB already allocated; 0 bytes free; 22.94 MiB reserved in total by PyTorch) 
If reserved memory is >> 
allocated memory try setting max_split_size_mb to avoid fragmentation. 
See documentation for Memory Management and PYTORCH_CUDA_ALLOC_CONF

webui.batに起動時のパラメーターを追加して再起動。

set PYTORCH_CUDA_ALLOC_CONF=garbage_collection_threshold:0.6,max_split_size_mb:128
set COMMANDLINE_ARGS=--lowvram --xformers

OutOfMemoryErrorからRuntimeErrorに変わりました。

Stable Diffusion WebUI

生成する画像サイズ等を最低レベルに変更して出力
OutOfMemoryErrorからRuntimeErrorに変わりました。

Stable Diffusion WebUI
RuntimeError: CUDA error: no kernel image is available for execution 
on the device CUDA kernel errors might be asynchronously reported at some other API call, 
so the stacktrace below might be incorrect. For debugging consider passing CUDA_LAUNCH_BLOCKING=1. 
Compile with `TORCH_USE_CUDA_DSA` to enable device-side assertions

結論

VRAM1GBでは生成できない。
10万円以下でVRAM12GB以上のグラボでコスパが高いGeForce RTX 3060を購入することにしました。


サクッと10分でChatGPT APIを試してみた

ChatGPTのAPIが提供開始されたので試してみました。

普段はちょとした調べものとか文章校正に使ってたのでAPIが提供されたことにより便利なツールが出てくるのではないかと期待してます。

注意点

APIを利用すると使用料金が発生します。
下記のページで確認しましたが、1000トークンで$0.002でした。

openai.com

使用量は入力トークンと出力トークンの合計で決まるようです。
ちなみに、「ChatGPTでできること」という語句で試してみると
トークン数は9個のトークン("Chat","G","PT","で","で","き","る","こ","と")に分割されました。

下記のページで現在の使用量等が確認できますが、$18.00分は無料枠のようです。

https://platform.openai.com/account/usage

使える言語

下記のページに各店言語のライブラリが確認できます。

https://platform.openai.com/docs/libraries

今回はPythonで実装することにします。

ライブラリのインストール

VSCODEのターミナルで下記コマンドを実行

PS C:\sources\chatgpt>pip install openai
Collecting openai
  Downloading openai-0.27.0-py3-none-any.whl (70 kB)
     |████████████████████████████████| 70 kB ...
Collecting tqdm
  Downloading tqdm-4.65.0-py3-none-any.whl (77 kB)
     |████████████████████████████████| 77 kB ...
・
・
・

API keyの作成

下記ページで API keyを作成します。

https://platform.openai.com/account/api-keys

API key

サンプルプログラム

import os
import openai

# Load your API key from an environment variable or secret management service
openai.api_key = "作成したAPI key"

response = openai.Completion.create(model="text-davinci-003",
 prompt="ChatGPTでできること", temperature=0.5, max_tokens=1024)
usage = response.usage
print("入力トークン数:" + str(usage.prompt_tokens))
print("出力トークン数:" + str(usage.completion_tokens))
print("トータルトークン数:" + str(usage.total_tokens))
print(response.choices[0].text.replace("。", "。\r\n"))

openai.api_keyに予め作成したAPI key、promptに質問したい内容(入力トークン)を指定します。

詳しくは下記ページを参照。

https://platform.openai.com/docs/api-reference/chat

実行結果

入力トークン数:9
出力トークン数:257
トータルトークン数:266


ChatGPTは、自然言語処理技術を使用して、質問に対して自然な文章を生成するチャットボットです。
ChatGPTは、ユーザーが質問を投稿したときに、質問に関連する文章を自動的に生成し、ユーザーの質問に対する自然な回答を行うことができます。
ChatGPTを使用すると、複雑な質問に対して、自然な文章を返すことができます。
また、質問に関連する情報を提示することもできます。

1000トークン$0.002で$18.00までが無料枠。
ということは9000000トークンまでは無料。
サンプル程度のトークンだと33834回は使えそうですね。

使用量の確認

下記のページで現在の使用量が確認できます。

https://platform.openai.com/account/usage

使用量


東証が公開している東証上場銘柄一覧を元に板情報を取得する

東証が公開している東証上場銘柄一覧

www.jpx.co.jp

こちらから東証上場銘柄一覧(Excel)をダウンロードすることができます。

freelancer.hatenablog.jp

このデータがあればau株コム証券のkabuステーション®APIを利用して東証の全銘柄のその日の板情報が取得できると思って試してみました。

kabuステーション®APIを利用する時の注意点

kabucom.github.io

情報系のリクエストは秒間10件ていどの流量制限が設けられている。
リクエストされた銘柄は自動でAPI登録銘柄リストに登録され、
50銘柄を超えると以降のリクエストが400 BadRequestになる。

使用するライブラリ

Excelを操作するためopenpyxlをインストールする。

Excelにヘッダ項目を追加

変更前のExcel

板情報取得に必要なのはB列のコード。

変更後のExcel

K列に安値、L列に高値、M列に始値、N列に終値、O列に出来高を追加してxlsxで保存する。
xlsだとopenpyxlで操作することができません。

サンプルプログラム1

B列のコードを取得していた情報を取得しK列~O列に設定。
B列がブランクになるまで処理してExcelを保存するサンプルです。
1件処理した後に0.2秒待機させてます。

import urllib.request
import json
import time

import openpyxl
from openpyxl.workbook import Workbook

# エクセルファイルのパス
EXCEL_PATH = "c:\\data\\東証上場銘柄一覧.xlsx"

class KabuStationApi():

    def __init__(self):
        """コンストラクタ
        """
        # APIトークン取得
        obj = { "APIPassword": "password"}
        url = "http://localhost:18080/kabusapi/token"
        res = self.__get_content(url, obj, {}, 'POST')
        self.token = res["Token"]

    def add_board_info(self):

        # 処理開始時刻
        start_time = time.time() 

        # Excelファイルを読み込む
        wb = openpyxl.load_workbook(filename=EXCEL_PATH, data_only=True)

        # シート
        ws = wb.worksheets[0]

        # 開始行番号
        rowNo = 2
        cd = ws.cell(row=rowNo, column=2).value

        # 銘柄コードがブランクになるまでループ
        while cd is not None:

            # 銘柄コード
            cd = str(ws.cell(row=rowNo, column=2).value)
            print(f"銘柄コード:{cd} Start")
            # 板情報取得用のURL 全て東証の銘柄なので市場コードは1固定
            url = "http://localhost:18080/kabusapi/board/" + str(cd) +"@1"
            param = {"X-API-KEY":self.token}
            res = self.__get_content(url, {}, param)
            if len(res) > 0:
                print(res["SymbolName"])
                # 安値
                ws.cell(row=rowNo, column=11, value=res["LowPrice"])
                # 高値
                ws.cell(row=rowNo, column=12, value=res["HighPrice"])
                # 始値
                ws.cell(row=rowNo, column=13, value=res["OpeningPrice"])
                # 終値
                ws.cell(row=rowNo, column=14, value=res["CurrentPrice"])
                # 出来高
                ws.cell(row=rowNo, column=15, value=res["TradingVolume"])
            print(f"銘柄コード:{cd} End")
           
            # 0.2秒待機
            time.sleep(0.2)
            
            # 次の行へ
            rowNo += 1
            cd = ws.cell(row=rowNo, column=2).value

        # 処理終了時刻
        end_time = time.time() 

        elapsed_time = end_time - start_time

        elapsed_hour = str(int(elapsed_time // 3600)).zfill(2)
        elapsed_minute = str(int((elapsed_time % 3600) // 60)).zfill(2)
        elapsed_second = str(int((elapsed_time % 3600 % 60))).zfill(2)

        wb.save(EXCEL_PATH)

        print(f"経過時間:{elapsed_hour}:{elapsed_minute}:{elapsed_second}:")

    def __get_content(self, url, body_param, header_param, method="GET"):
        """指定したURLにリクエストを送信しレスポンスを返す

        Parameters
        ----------
        url : string
            url
        body_param : dict, optional
            Httpリクエストボディに設定するパラメーター, by default None
        header_param : dict, optional
            Httpヘッダに設定するパラメーター, by default {}
        method : str, optional
            HTTPメソッド, by default 'GET'

        Returns
        -------
        dict
            取得したデータ
        """
        req = None
        if body_param is not None:
            req = urllib.request.Request(url, json.dumps(body_param).encode('utf8'), method=method)
        else:
            req = urllib.request.Request(url, method=method)
        req.add_header('Content-Type', 'application/json')
        for key, param in header_param.items():
            req.add_header(key, param)
        try:
            with urllib.request.urlopen(req) as res:
                return json.loads(res.read())
        except urllib.error.HTTPError as e:
            print(e)
            return {}
        except Exception as e:
            print(e)
            return {}

# インスタンス生成
api = KabuStationApi()

# データを取得しExcelの板情報を更新
api.add_board_info()

実行結果

・
・
・
・
銘柄コード:1390 Start
UBS ETF MSCIアジア太平洋株(除く日本)
銘柄コード:1390 End
銘柄コード:1391 Start
HTTP Error 400: Bad Request
銘柄コード:1391 End
銘柄コード:1392 Start
HTTP Error 400: Bad Request
銘柄コード:1392 End
銘柄コード:1393 Start
HTTP Error 400: Bad Request
銘柄コード:1393 End
銘柄コード:1394 Start
HTTP Error 400: Bad Request
銘柄コード:1394 End
API登録銘柄リスト

リファレンスに記載された通りAPI登録銘柄リストに登録された銘柄が50銘柄を超えると以降のリクエストが400 BadRequestになります。

kabucom.github.io

こちらのリファレンスを参考に50件登録したらAPI登録銘柄リストをクリアするようにすればBad Requestは解消されました。

サンプルプログラム2

サンプルプログラム1にAPI登録銘柄リストクリア処理を追加したサンプルです。

import urllib.request
import json
import time

import openpyxl
from openpyxl.workbook import Workbook

# エクセルファイルのパス
EXCEL_PATH = "c:\\data\\東証上場銘柄一覧.xlsx"

class KabuStationApi():

    def __init__(self):
        """コンストラクタ
        """
        # APIトークン取得
        obj = { "APIPassword": "password"}
        url = "http://localhost:18080/kabusapi/token"
        res = self.__get_content(url, obj, {}, 'POST')
        self.token = res["Token"]
        print("APIトークン:" + self.token)

    def add_board_info(self):

        # 処理開始時刻
        start_time = time.time() 

        # Excelファイルを読み込む
        wb = openpyxl.load_workbook(filename=EXCEL_PATH, data_only=True)

        # シート
        ws = wb.worksheets[0]

        # 開始行番号
        rowNo = 2
        cd = ws.cell(row=rowNo, column=2).value

        # 処理前にAPI登録銘柄リストをクリア
        cnt = 0
        self.__clear_register()

        # 銘柄コードがブランクになるまでループ
        while cd is not None:

            # 銘柄コード
            cd = str(ws.cell(row=rowNo, column=2).value)
            print(f"銘柄コード:{cd} Start")
            # 板情報取得用のURL 全て東証の銘柄なので市場コードは1固定
            url = "http://localhost:18080/kabusapi/board/" + str(cd) +"@1"
            param = {"X-API-KEY":self.token}
            res = self.__get_content(url, {}, param)
            if len(res) > 0:
                print(res["SymbolName"])
                # 安値
                ws.cell(row=rowNo, column=11, value=res["LowPrice"])
                # 高値
                ws.cell(row=rowNo, column=12, value=res["HighPrice"])
                # 始値
                ws.cell(row=rowNo, column=13, value=res["OpeningPrice"])
                # 終値
                ws.cell(row=rowNo, column=14, value=res["CurrentPrice"])
                # 出来高
                ws.cell(row=rowNo, column=15, value=res["TradingVolume"])
            print(f"銘柄コード:{cd} End")
           
            # 0.2秒待機
            time.sleep(0.2)
            
            # 次の行へ
            rowNo += 1
            cd = ws.cell(row=rowNo, column=2).value

            cnt += 1

            # API登録銘柄が50になった場合登録状態をクリアする
            if cnt == 50:
                cnt = 0
                self.__clear_register()

        # 処理終了時刻
        end_time = time.time() 

        elapsed_time = end_time - start_time

        elapsed_hour = str(int(elapsed_time // 3600)).zfill(2)
        elapsed_minute = str(int((elapsed_time % 3600) // 60)).zfill(2)
        elapsed_second = str(int((elapsed_time % 3600 % 60))).zfill(2)

        wb.save(EXCEL_PATH)

        print(f"経過時間:{elapsed_hour}:{elapsed_minute}:{elapsed_second}:")

    def __get_content(self, url, body_param, header_param, method="GET"):
        """指定したURLにリクエストを送信しレスポンスを返す

        Parameters
        ----------
        url : string
            url
        body_param : dict, optional
            Httpリクエストボディに設定するパラメーター, by default None
        header_param : dict, optional
            Httpヘッダに設定するパラメーター, by default {}
        method : str, optional
            HTTPメソッド, by default 'GET'

        Returns
        -------
        dict
            取得したデータ
        """
        req = None
        if body_param is not None:
            req = urllib.request.Request(url, json.dumps(body_param).encode('utf8'), method=method)
        else:
            req = urllib.request.Request(url, method=method)
        req.add_header('Content-Type', 'application/json')
        for key, param in header_param.items():
            req.add_header(key, param)
        try:
            with urllib.request.urlopen(req) as res:
                return json.loads(res.read())
        except urllib.error.HTTPError as e:
            print(e)
            return {}
        except Exception as e:
            print(e)
            return {}

    def __clear_register(self):
        """API登録銘柄リストをクリアする
        """
        url = "http://localhost:18080/kabusapi/unregister/all"
        param = {"X-API-KEY":self.token}
        print("登録銘柄クリア Start")
        res = self.__put_content(url, {}, param)
        print("登録銘柄クリア End")
        return res

    def __put_content(self, url, body_param, header_param):
        """指定したURLにリクエストを送信しレスポンスを返す

        Parameters
        ----------
        url : string
            url
        body_param : dict, optional
            Httpリクエストボディに設定するパラメーター, by default None
        header_param : dict, optional
            Httpヘッダに設定するパラメーター, by default {}
        method : str, optional
            HTTPメソッド, by default 'GET'

        Returns
        -------
        dict
            取得したデータ
        """
        if body_param is not None:
            req = urllib.request.Request(url, json.dumps(body_param).encode('utf8'), method="PUT")
        else:
            req = urllib.request.Request(url, method="PUT")
        req.add_header('Content-Type', 'application/json')
        for key, param in header_param.items():
            req.add_header(key, param)
        try:
            with urllib.request.urlopen(req) as res:
                return json.loads(res.read())
        except urllib.error.HTTPError as e:
            print(e)
            return {}
        except Exception as e:
            print(e)
            return {}

# インスタンス生成
api = KabuStationApi()

# データを取得しExcelの板情報を更新
api.add_board_info()

実行結果

・
・
・
・
銘柄コード:9993 Start
ヤマザワ
銘柄コード:9993 End
銘柄コード:9994 Start
やまや
銘柄コード:9994 End
銘柄コード:9995 Start
グローセル
銘柄コード:9995 End
銘柄コード:9996 Start
サトー商会
銘柄コード:9996 End
銘柄コード:9997 Start
ベルーナ
銘柄コード:9997 End
経過時間:05:57:15:
Excel 板情報反映後
Excel 板情報反映後

一部ブランクの銘柄がありまが、おそらく出来高0の銘柄。
0.2秒待機しましたが、レスポンスが返ってくるまで数秒かかったので全銘柄取得するのに約6時間かかりました・・・

試す場合は待機時間等に気を付けて自己責任でお願いします。


au 株コム証券のkabuステーション®APIで株価情報を取得してみる

取引した銘柄の最新の株価を取得したい

数年前からExcelで投資した取引情報等を記録してるのですが、
たまに過去に売却した株をずっと保有していればどうなっていたか?
と思うことがあり、その都度証券会社にログインしてその日の株価を確認してExcelに転記して確認してました。

取引履歴が多くなってきたので手動でやると時間がかかってしまうので自動化することに。
当初は株探等をスクレイピングして最新の株価を取得するようにしました。

freelancer.hatenablog.jp

スクレイピング自体がグレーな感じなのとHPのレイアウトが変更されたりすると
プログラムの修正が発生する。
ということでauカブコム証券が提供するAPIを利用することにしました。

kabuステーション®API利用設定

kabuステーションAPI利用申請

kabuステーション、kabuステーションAPIを利用するためにau 株コム証券にログインして申請します。

注意点1

kabuステーションの通常プランの利用に990円(税込)/月 が必要になります。
初回申込みの翌々月第1営業日まで無料。
その後も預かり資産や取引状態によって無料で利用を続けることも可能。

注意点2

kabuステーションAPIはFintechプランが適用されないと利用不可。
Fintechプランは信用取引口座や先物オプション取引口座の開設状況、取引状態によって適用するか判定される。
初回申込みの翌々月第1営業日までFintechプランが適用される。

kabuステーションのダウンロードとインストール

下記よりsetup.exeをダウンロードしてインストール。
download.r10.kabu.co.jp

kabuステーションのダウンロード

APIシステム設定

kabuステーションを起動する。

kabuステーションのログイン画面

au 株コム証券の口座番号とパスワードを入力してログインする。

kabuステーション

右上の</>を右クリック→APIシステム設定を選択

APIシステム設定を選択
APIシステム設定

APIを利用する」をチェックし、「APIパスワード(本番用)」、「APIパスワード(検証用)」に任意のパスワードを入力。
入力したパスワードは認証時のパスワードとして利用します。
設定後にkabuステーションを再起動する。

APIが利用可能か確認

</>が緑色になればAPIが利用可能。

※ kabuステーション経由でデータを取得するため起動していないとデータは取得できません。

プログラムを作成するために参考にしたページ

REST API リファレンス
kabucom.github.io

サンプルコード
github.com

認証後に指定した銘柄の板情報を取得するサンプル

イオンの板情報を取得するPythonのサンプルプログラム。

import urllib.request
import json
import pprint

# kabuステーションのAPIシステム設定で入力したAPIパスワード(本番用)を指定
obj = { 'APIPassword': 'password' }
json_data = json.dumps(obj).encode('utf8')

# APIトークン取得用のURL
# リファレンス:https://kabucom.github.io/kabusapi/reference/index.html#tag/auth
url = 'http://localhost:18080/kabusapi/token'
# パラメーターをHTTPリクエストメッセージボディに設定
req = urllib.request.Request(url, json_data, method='POST')
req.add_header('Content-Type', 'application/json')
token = ""
try:
    with urllib.request.urlopen(req) as res:
        content = json.loads(res.read())
        # APIトークン
        token = content["Token"]
except urllib.error.HTTPError as e:
    print(e)
    content = json.loads(e.read())
    pprint.pprint(content)
except Exception as e:
    print(e)

# 板情報取得用のURL
# http://localhost:18080/kabusapi/symbol/{symbol}
# symbolには銘柄コード@市場コードを指定、イオンの場合は東証なので市場コードは1、銘柄コードは8267
# リファレンス:https://kabucom.github.io/kabusapi/reference/index.html#operation/boardGet
url = 'http://localhost:18080/kabusapi/board/8267@1'
req = urllib.request.Request(url, method='GET')
req.add_header('Content-Type', 'application/json')
# APIトークンをHTTPヘッダに設定
req.add_header('X-API-KEY', token)
try:
    with urllib.request.urlopen(req) as res:
        content = json.loads(res.read())
        print("現値:" + str(content["CurrentPrice"]))
        print("前日終値:" + str(content["PreviousClose"]))
        print("前日比:" + str(content["ChangePreviousClose"]))
        print("騰落率:" + str(content["ChangePreviousClosePer"]))
        print("始値:" + str(content["OpeningPrice"]))
        print("高値:" + str(content["HighPrice"]))
        print("安値:" + str(content["LowPrice"]))
        print("出来高:" + str(content["TradingVolume"]))
except urllib.error.HTTPError as e:
    print(e)
    content = json.loads(e.read())
    pprint.pprint(content)
except Exception as e:
    print(e)

2023/02/13に実行した結果

現値:2648.5
前日終値:2617.0   
前日比:31.5       
騰落率:1.2        
始値:2625.0       
高値:2654.5       
安値:2616.5       
出来高:2042800.0
イオンの個別銘柄情報

認証→板情報取得の流れで板情報が取得できました。
今のところ板情報以外必要ありませんが、発注処理もできるので板情報と合わせて自動売買なんかもできそうですね。
個人的には条件指定して該当する銘柄一覧が取得できるような機能があれば利用したいけどリファレンスをみた限りではなさそうですね。