ひゃまだのblog

ひゃまだ(id:hymd3a)の趣味のブログ

遺伝的プログラミングで関数式のあてはめ

(2019-02-22 初稿 - 2021-05-21 転記・修正 )


はじめに

CQ出版社のインターフェース(2018年3月号)に、「遺伝条件を設定して突然変異も起こさせる発展形【遺伝的プログラミング」(牧野 浩二、小林裕之 著)】が掲載されていた。以前、遺伝的アルゴリズムナップサック問題に挑戦したことがあるので、この記事がとても読みやかったとともに、自分でもプログラミングしてみたくなった。

記事に掲載されたプログラムのダウンロードは以下のページからできるので、2018年3月号のところからダウンロードし試してみて。

上記プログラムは、JAVAで記述されており、自分自身の勉強のためにも、rubyスクリプトを作成してみた。

(2021-05-21 追記)

PythonでTree構造を操作するバージョンを作ろうとしているが、いつまで経ってもできないヘタレな筆者。(-_-;)

このページでは、作成したスクリプトの紹介と、実際に関数式のあてはめを実施するときのコツなどを紹介する。

なお、最初に申し上げるが、最初はがんばってTree構造を使って、個体(遺伝子)を生成したが、交配(交叉)や突然変異は、Tree構造の関数式を文字列として行っているので、プログラミング的にはまったく参考にならない。あらかじめご了承を。m(_ _)m

遺伝的プログラミングとは

遺伝的プログラミングは、遺伝的アルゴリズムと良く似た手法で解を求める手法。
遺伝的アルゴリズムは、ナップサック問題(ナップサックの中に効率的に荷物を詰め込む問題)に代表されるもので、ある荷物を入れるか入れないかで体積の増減を決まった値でしか変動させることしかできない。
一方、遺伝的プログラミングは、体積など固有の値だけでなく、変数や条件式をいれることができるので、より複雑な解を求めることができるようだ。

以下のサイトに非常にわかりやすく解説されていたので参照を。

手法は、まずは個体(遺伝子)を作成し、評価関数により個体を評価する。
続いて、評価により優秀な個体を中心に、他の優秀な個体と交配(交叉)させる。
また、自然界と同様にある頻度で、突然変異も起こさせて、さらに優秀な個体、言い換えれば優良な解を得やすくするアルゴリズム

作成したプログラム

実際に、作成したプログラムは以下のとおり。

rubyの開発環境がある方は、リンク先のrubyスクリプトをダウンロードして実行を。

$ mv geneprg.rb.txt geneprg.rb
$ chmod +x geneprg.rb

& ./geneprg.rb

また、サンプルデータもダウンロードして、rubyスクリプトと同じフォルダに保存する。
ちなみに、データファイルは、以下のようにxの値とyの値を[,](カンマ)で区切って記述し、データは前述のインターフェースの記事のものを流用させていただいた(多謝)。

# x の値 , y の値
# 解 y = -3 x^4 + 4 x^3 + 12 x^2
# 行頭の#はコメント行
-3, -243
-2, -32
-1, 5
0, 0
1, 13
2, 32
3, -27
4, -320

rubyスクリプトのソース (本来は、お見せできるレベルのものではないが… (^^ゞ)

#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-
# generuby.rb 
# 遺伝的プログラミングの実装テスト
# 最適な関数を探す
# ver 0.01 2019-01-23 start - 2019-02-13
# ver 0.04 2019-02-22 データファイル読み込み、NaN対策
#
=begin
Interface CQ出版社 2018年3月号 「人工知能アルゴリズム探索隊」より
  y = -3x^4 + 4x^3 + 12x^2
$x = [  -3,  -2, -1, 0,  1,  2,   3,    4]
$y = [-243, -32,  5, 0, 13, 32, -27, -320]

Node と Leaf の2種類がある

  Node のタイプ  + - * / ^
  Leaf のタイプ  x 0〜10の数値 (左右2つを必ず持つ)

tree は node を一つ以上もち、leaf を2つ持つ
leaf は、nodeの場合とleafの場合がある
=end

$KCODE = "UTF8" if RUBY_VERSION < '1.9.0'

require 'rubygems'

# 定数(パラメータ)定義
MAX_DEPTH = 20        # 階層の深さ
POPULATION = 2000     # 1世代当たりの個体数
MUTATION_RATE = 0.1   # 突然変異率
MATE_RATE = 0.4       # 交配対象の率 例 上位6割まで
MAX_CROSS_RATE = 0.3  # 交配率
MAX_GENERATION = 500  # 最大世代数

$max_cross = POPULATION * MAX_CROSS_RATE   # 最大交配組合せ数  交配後の個体はその2倍
$mate_num = POPULATION* MATE_RATE          # 交配の相手となり得る個体  例)上位30まで
$max_mutation = POPULATION * MUTATION_RATE # 突然変異を起こす個体数

# node の種類と確率 合計100になるように  ^ は ruby では **
#$node_hash = { "+" => 24, "-" => 24, "*" => 24, "/" => 24, "^" => 4 }
$node_hash = { "+" => 25, "-" => 25, "*" => 25, "/" => 25 }
# leaf の種類と確率 合計100になるように i の場合は 9までの整数
$leaf_hash = { "x" => 25, "n" => 25, "i" => 50 }
# max_depth のときの leaf の種類と確率 合計100になるように i の場合は 9までの整数
$max_depth_leaf_hash = { "x" => 25, "i" => 75 }

USAGE = <<'EOS'
[Usage] geneprg.rb datafile.txt
  dataファイルを指定しない場合は、カレントディレクトリの gp_data.txtを用いる

  datafile フォーマット
  # 先頭行の#はコメント
  data_x1, data_y1
  data_x2, data_y2
  data_x3, data_y3

EOS

# x と y の値  
$x = Array.new
$y = Array.new

# hash を受けて、確率に基づく文字を返す
def prob_ch(m_hash)
  range_hash = Hash.new()

  sum = 0
  m_hash.each{ |key, value| 
    sum += value
    range_hash[key] = sum
  }

  num = rand(sum)
  range_hash.each{ | key, value |
    if num < value
      return key
    end
  }
end

class Tree
  attr_accessor :node_value     # node の 演算子
  attr_accessor :lleaf          # node の 左下 leaf
  attr_accessor :rleaf          # node の 右下 leaf
  attr_accessor :sqerr          # 誤差の二乗和
  attr_accessor :funcstr        # 関数の文字列

  def initialize
    @node_value = prob_ch($node_hash)
    @lleaf = prob_ch($leaf_hash)
    @lleaf = rand(10) if @lleaf == "i"
    @rleaf = prob_ch($leaf_hash)
    @rleaf = rand(10) if @rleaf == "i"
    @sqerr = 0.0
    @funcstr = ""
  end
end

def make_tree(depth)
  tree = Tree.new

  while tree.lleaf == "n" || tree.rleaf == "n"
      if depth == MAX_DEPTH
        tree.lleaf = prob_ch($max_depth_leaf_hash)
        tree.lleaf = rand(10) if tree.lleaf == "i"
        tree.rleaf = prob_ch($max_depth_leaf_hash)
        tree.rleaf = rand(10) if tree.rleaf == "i"
        break
      end
      tree.lleaf = make_tree(depth) if tree.lleaf == "n"
      tree.rleaf = make_tree(depth) if tree.rleaf == "n"
      depth += 1
  end
  return tree
end

# function を string に 
# 各項間にスペース 
def func_str(t)
  str = "("
  if t.lleaf.class == Tree
    str += " " + func_str(t.lleaf)
  else
    str += " " + t.lleaf.to_s
  end
  str += " " + t.node_value
  if t.rleaf.class == Tree
    str += " " + func_str(t.rleaf)
  else
    str += " " + t.rleaf.to_s
  end
  str += " " + ")"
  return str
end

def calc_sqerr(value, y)
  return (value - y)**2
end

# 力技で、指定する()を切り取る 
def cut_func(str, cutp)
  fn = [ "", "", "" ]   # 0: pre  1: match   2: after
  i_num = v_num = 0     # cutp ( num  v_num ()の増減
  inflag = "p"          # [p] pre [m] match [a] after
  str.scan(/./){|ch|
    case ch
    when "(" then
      if i_num == cutp
        inflag = "m"
        i_num += 1
        fn[1] += ch     # match
      else
        case inflag
        when "p"
          i_num += 1
          fn[0] += ch
        when "m"
          v_num += 1
          fn[1] += ch
        else
          fn[2] += ch
        end
      end
    when ")" then
      case inflag
      when "p"
        fn[0] += ch
      when "m"
        fn[1] += ch
        inflag = "a" if v_num == 0
        v_num -= 1
      else
        fn[2] += ch
      end
    else
      case inflag
      when "p"
        fn[0] += ch
      when "m"
        fn[1] += ch
      else
        fn[2] += ch
      end
    end
  }
  return fn
end

# 初期個体の生成
def init_tree(tree)
  num = 0
  while num < POPULATION
    tree[num] = make_tree( 0 )
    tree[num].funcstr = func_str(tree[num])
    #next if tree[num].funcstr.count("(") < 2 
    if tree[num].funcstr.match('x')
      num += 1
      #p "OK"
    else
      next
    end
  end
  return tree
end

# ソート
def sort_tree(tree)
  tree = tree.sort{|a, b| a.sqerr <=> b.sqerr}
  return tree
end

# cross
def cross_tree(tree)
  for i in 0...$max_cross do   # max_crossの数は含まない max_cross - 1
    j = rand($mate_num)
    i_node, j_node  = tree[i].funcstr.count("("), tree[j].funcstr.count("(")
    funcstr1 = funcstr2 = ""
    icutp, jcutp = rand(i_node), rand(j_node)
    p1 = cut_func(tree[i].funcstr, icutp)
    p2 = cut_func(tree[j].funcstr, jcutp)
    funcstr1 = p1[0] + p2[1] + p1[2]
    funcstr2 = p2[0] + p1[1] + p2[2]
    while funcstr1.count("(") > MAX_DEPTH * 2
      funcstr1 = funcstr1.sub(/\([!-~ ]{7}\)/, rand(10).to_s)
    end
    while funcstr2.count("(") > MAX_DEPTH * 2
      funcstr2 = funcstr2.sub(/\([!-~ ]{7}\)/, rand(10).to_s)
    end
    tree[POPULATION - 1 - 2 * i    ].funcstr = funcstr1
    tree[POPULATION - 1 - 2 * i - 1].funcstr = funcstr2
  end 
  return tree
end

# 突然変異
def mute_tree(tree)
  for i in 0...($max_mutation) do
    j = rand(POPULATION)
    chnum = tree[j].funcstr.scan(/\d+|x/).length
    mp = rand(chnum)
    atree = make_tree(MAX_DEPTH)
    atree.funcstr = func_str(atree)
    rfunc = ""
    cnt = 1
    tree[j].funcstr.scan(/./){ |ch|
      if ch =~ /\d+/ || ch == "x"
        if cnt == mp
          rfunc += atree.funcstr
        else
          rfunc += ch
        end
        cnt += 1
      else
        rfunc += ch
      end
    }
    tree[j].funcstr = rfunc
    return tree
  end
end

# 2乗和の計算
def sum_sqerr(fstr)
  fvalue = sqerr = 0
  for i in 0...$x.length do
    fxstr = fstr.gsub(/x/, "#{$x[i].to_s}").gsub(/\^/, "**")
    begin
      fvalue = eval("#{fxstr}")
      fvalue = Float::INFINITY  if fvalue.nan?
    rescue => e
      s = Float::INFINITY    # 無限大
    end
    sqerr += calc_sqerr(fvalue.to_f, $y[i])
  end
  return sqerr
end

# main loop
# 最大 loop 回数になるまで実行
def main()
  
  if ARGV.size == 0
    dfile = "gp_data.txt"
  elsif ARGV[0].to_s == "-h" || ARGV[0].to_s == "-help"
    puts USAGE
    exit
  else 
    dfile = ARGV[0].to_s
  end

  File.open("#{dfile}"){ |f|
    while line = f.gets
      next if line[0] == "#" || !line.index(/[0-9]/)
      line = line.rstrip.split(",")
      $x.push(line[0].to_f)
      $y.push(line[1].to_f)
    end
  } 
  
  tree = Array.new(POPULATION)

  tree = init_tree(tree)

  tree.each { | t | t.sqerr = sum_sqerr(t.funcstr) }

  tree = sort_tree(tree)

  i = 1
  until i > MAX_GENERATION do
    tree = cross_tree(tree)
    tree = mute_tree(tree)
    tree.each { | t | t.sqerr = sum_sqerr(t.funcstr) }
    tree = sort_tree(tree)
    print i, " times ", tree[0].sqerr, " : ", tree[0].funcstr, "\n"
    break if tree[0].sqerr == 0.0
    i += 1
  end

  print tree[0].sqerr, " : ", tree[0].funcstr, "\n"
end

if __FILE__ == $0
  main
end

実行すると、その世代のトップの個体の2乗和(ばらつき)とその数式が表示される。

1 times 100400.0 : ( ( 2 - 7 ) * ( x * x ) )
 (中略)
50 times 902.72 : ( ( ( ( ( ( ( x * ( 3 - x ) ) - ( x * x ) ) * ( x * x ) ) * 1 ) - ( ( ( 3 - x ) * x ) - 6 ) ) + ( ( 3 - x ) * 2 ) ) - ( ( ( x * ( ( ( ( ( ( ( ( x * x ) - 7 ) - 6 ) * x ) * 1 ) / 5 ) * x ) - 6 ) ) - 8 ) * 1 ) )
  (中略)
96 times 0.0 : ( ( ( ( ( ( x * ( 3 - x ) ) - ( x * x ) ) * ( x * x ) ) - ( ( ( ( ( ( x * x ) - 7 ) - 6 ) * x ) * x ) - ( x * x ) ) ) - ( x * ( 3 - x ) ) ) + ( x * ( ( 3 - x ) - ( ( 2 - x ) * x ) ) ) )

上記の例では、96世代目で解に到達したが、解に到達できないことが多い。
筆者が行った数少ない経験から、解に到達する確率を上げる方法について、次節で述べる。

Tips
これまでの経験から、解に到達する上で、重要と感じられたパラメータを以下に示す。

・1世代あたりの個体数 ⇒ 個体数が多いほど、解に到達しやすい
     POPULATION = 2000     # 1世代当たりの個体数
・ 階層の深さ ⇒ ある程度深い方が、解に到達しやすい
     MAX_DEPTH = 20        # 階層の深さ
・ 交配率が高いほど、解に到達しやすい
     MAX_CROSS_RATE = 0.3  # 交配率
・ 世代は多い方が、解に到達しやすい
     MAX_GENERATION = 500  # 最大世代数

いずれにしても、スクリプトの最初の方にあるパラメータを変化させてチャレンジしてみて。
というのも、遺伝的プログラミングは、局所解に陥りやすい特徴があるため、パラメータの調整は、必須だと思って。

得られた解が、カッコが多くて非常に読みにくいが、Raspberry Pi等をお持ちの場合は、Walform Mathematicaが付属しているので、下図のようにExpandコマンドで簡単に式を展開することができる。

f:id:hymd3a:20210521110758p:plain

Expandによる式展開

この例では、もともとの式が y = -3 x^4 + 4 x^3 + 12 x^2 だったが、式を展開してみてもバッチリ正解だった。

なお、Mathematica は、流石に高級なソフトウェアだけあって、きれいにグラフを描いてくれる。
例えば、y = -3 x^4 + 4 x^3 + 12 x^2 をグラフにするときは、以下のとおり Plot コマンドを用いる。

Plot[ { -3 x^4 + 4 x^3 -12 x^2}, {x, -3, 3}]         #  xを -3から+3まで変化させる

f:id:hymd3a:20210521110932p:plain

Mathematicaによるグラフ描画

 

おわりに

足早だったが、ひととおり遺伝的プログラミングについて解説した。
くどいようだが、このアルゴリズムは、局所解に陥りやすいので、パラメータを変更して何度も試してみること。

その際に、Mathematicaのようにグラフ化すると、イメージがつかみやすくて良い。

また、何かわかりましたら、記述するね。

関連ページ

Tensorflow で回帰分析

(2018-02-19 初稿 - 2021-05-21 転記・修正)

はじめに

Raspberry Pi 3(以下、ラズパイ)を購入してから、python や tensorflow を使って、ディープラーニングのことを学んでいる。
ちなみに、筆者は、ディープラーニングのことも、pythonのことも、まったくの素人なので、以下の記述は参考までに。

なお、tensorflowのラズパイへのインストールは以下のページを参照のこと。

さて、tensorflowをgoogleから頂いて来て、MNIST dataでmnist_softmax.pyを実行して、文字認識のテストをしたまでは良かったが、実際何をやっているのか筆者にはまったく理解できなかった。とほほ。(^_^;)

まずは、理解しやすい内容を実行しようと思い、回帰分析を実行してみた。

このページでは、tensorflowを用いた回帰分析について記述する。

なお、このページを記述するにあたり、以下のサイトを参照させていただいた。多謝

1次回帰

まずは、上記サイトの1次回帰のスクリプトを実行してみる。

# coding:utf-8

import tensorflow as tf

# y = 2x + 1
input_x = [[0.],[1.]]
input_y = [[1.],[3.]]

x = tf.placeholder("float", [None, 1])
y_ = tf.placeholder("float", [None, 1])

a = tf.Variable([1.], name="slope")        # 初期値 a = 1
b = tf.Variable([0.], name="y-intercept")  # 初期値 b = 0
y = tf.multiply(a,x) + b

init = tf.global_variables_initializer()

# 誤差関数
loss = tf.reduce_sum(tf.square(y_ - y))

# トレーニング方法は、勾配降下法を選択
train_step = tf.train.GradientDescentOptimizer(0.03).minimize(loss)

with tf.Session() as sess:
        sess.run(init)
        print("初期状態")
        print('誤差' + str(sess.run(loss, feed_dict={x: input_x, y_: input_y})))
        print("slope: %f, y-intercept: %f" % (sess.run(a), sess.run(b)))

        for step in range(200):
            sess.run(train_step, feed_dict={x: input_x, y_: input_y})
            if (step+1) % 20 == 0:
                print('\nStep: %s' % (step+1))
                print('誤差' + str(sess.run(loss, feed_dict={x: input_x, y_: input_y})))
                print("slope: %f, y-intercept: %f" % (sess.run(a), sess.run(b)))

実行結果は以下のとおり。

(tflow) $ python 1kaiki.py 
初期状態
誤差5.0
slope: 1.000000, y-intercept: 0.000000

Step: 20
誤差0.02128378
slope: 1.802436, y-intercept: 1.069051

(中略)

Step: 200
誤差3.7843301e-06
slope: 1.997323, y-intercept: 1.001655

正解は、y=2x+1 の式だが、200回のステップで、正解の値までかなり近づくことがわかった。

2次回帰

1次回帰については、なんとかできるようになったので、2次回帰に挑戦してみる。前回のリストから、値の部分と、計算式の部分が異なるだけ。

【List 2-1】

# coding:utf-8

import tensorflow as tf

input_x = [[-1.],[0.],[2]]
input_y = [[3.],[1.],[9]]

x = tf.placeholder("float", [None, 1])
y_ = tf.placeholder("float", [None, 1])

a = tf.Variable([1.], name="slope")
b = tf.Variable([0.], name="y-intercept")
y = tf.multiply(a,x**2) + b                       # y = 2 x^2 + 1

init = tf.global_variables_initializer()

# 誤差関数
loss = tf.reduce_sum(tf.square(y_ - y))

# トレーニング方法は、勾配降下法を選択
train_step = tf.train.GradientDescentOptimizer(0.03).minimize(loss)

with tf.Session() as sess:
        sess.run(init)
        print("初期状態")
        print('誤差' + str(sess.run(loss, feed_dict={x: input_x, y_: input_y})))
        print("slope: %f, y-intercept: %f" % (sess.run(a), sess.run(b)))

        for step in range(200):
            sess.run(train_step, feed_dict={x: input_x, y_: input_y})
            if (step+1) % 20 == 0:
                print('\nStep: %s' % (step+1))
                print('誤差' + str(sess.run(loss, feed_dict={x: input_x, y_: input_y})))
                print("slope: %f, y-intercept: %f" % (sess.run(a), sess.run(b)))

実行結果は以下のとおり。

(tflow) @ ~/tflow$ python 2kaiki_list2-1.py 
初期状態
誤差30.0
slope: 1.000000, y-intercept: 0.000000

Step: 20
誤差0.017613236
slope: 2.034258, y-intercept: 0.893097

(中略)

Step: 160
誤差1.1404211e-12
slope: 2.000000, y-intercept: 1.000000

なんと、160ステップでほぼ正解に辿りついた。

もう少し実用的な回帰分析ができないと、tensorflowを使う意味がないね。

そこで、ネットで検索して、年と総農家個数の表を見つけたので、以下のとおりデータファイルを作成した。 毎回、データを打ち込むのって大変だからね。

# test data 年(年-1900/10)と総農家戸数(1/1000)
6.0,2.078
6.5,1.219
7.0,0.831
7.5,0.616
8.0,0.623
8.5,0.626
9.0,0.498
9.5,0.473

注意点としては、あまり大きな数字だと、nanとエラーが出るので、年については (年 - 1900)/10、総農家戸数は1/1000に値を小さくしている。

さっそく、データファイルから読み込むスクリプトを作成。

【List 2-2】

# coding:utf-8

import tensorflow as tf
import sys

args = sys.argv               # コマンドライン引数を取得

if len(args) == 1:            # 引数がなかったら
    fname = "./data.csv"      # data.csv を読む
else:                         # 引数があったら
    fname = args[1]           # ファイルを指定

input_x = []
input_y = []

f = open(fname)
for line in f.readlines():
    if line[0] == '#':         # データファイルのコメントスキップ
        continue
    line = line.split(',')     # カンマで分けて読み込む
    input_x.append([float(line[0])])
    input_y.append([float(line[1])])

x = tf.placeholder("float", [None, 1])
y_ = tf.placeholder("float", [None, 1])

a = tf.Variable([1.], name="slope")
b = tf.Variable([0.], name="y-intercept")
y = tf.multiply(a,1/tf.sqrt(x)) + b     # y = a / sqrt(x) + b に近い

init = tf.global_variables_initializer()

# 誤差関数
loss = tf.reduce_sum(tf.square(y_ - y))

# トレーニング方法は、勾配降下法を選択
train_step = tf.train.GradientDescentOptimizer(0.03).minimize(loss)

with tf.Session() as sess:
        sess.run(init)
        print("初期状態")
        print('誤差' + str(sess.run(loss, feed_dict={x: input_x, y_: input_y})))
        print("slope: %f, y-intercept: %f" % (sess.run(a), sess.run(b)))

        for step in range(400):
            sess.run(train_step, feed_dict={x: input_x, y_: input_y})
            if (step+1) % 20 == 0:
                print('\nStep: %s' % (step+1))
                print('誤差' + str(sess.run(loss, feed_dict={x: input_x, y_: input_y})))
                print("slope: %f, y-intercept: %f" % (sess.run(a), sess.run(b)))

実行結果は以下のとおり。

(tflow) @ ~/tflow$ python 2kaiki_list2-2.py 
初期状態
誤差3.94166
slope: 1.000000, y-intercept: 0.000000

Step: 20
誤差1.8281436
slope: 1.259537, y-intercept: 0.417840

(中略)
Step: 400
誤差1.5395262
slope: 2.963965, y-intercept: -0.199980

400ステップで、誤差は大分小さくなったが、y = a / sqrt(x) + b の式に適合しているかは、検定しないとわからないが、近いような気もする。(^_^;)

おわりに

今回は、2次回帰までのスクリプトを作ったが、本来ならばディープラーニングで、入力した式も自ら推定しながら解析して欲しいよね。
自ら解析しながら、式を作っていく方法については、以下に示すので参考に。^^;

また、何かわかったら、追記するね。

関連ページ

Raspberry Pi 3 で ディープラーニング(tensorflow)

(2018-02-12 初稿 - 転記・修正 2021-05-20)

はじめに

これまで、Raspberry Pi 3(以下、ラズパイ) に色々機能を持たせてきたが、今度はディープラーニングをやってもらう。
筆者は、ディープラーニングのことは全くの素人だが、挑戦だけはしたいのだ。(^_^;)
で、ディープラーニングの実装ライブラリとして最も有名なのが、Google謹製の tensorflow。

なお、設定時の筆者のラズパイの環境は、以下のとおり。

pi@raspberrypi:~ $ cat /etc/debian_version
9.3

tensorflowのインストール

本家のgoogleRaspberry PiのRaspbian 9.3以降を正式サポートしたので、インストール方法が変更したとともに、python2からpytho3へと環境が変更した。

インストールの参考にしたページは以下のとおり。多謝

まずは、python3.5のpipや開発環境を整える。

# For Python 3.5
sudo apt-get install python3-pip python3-dev

必要なパッケージをインストールした後に、tensorflowをインストールする。
なお、tensorflowのインストールには、かなり時間が掛かったので、あせらずに、ゆっくり待とう。

sudo apt install libatlas-base-dev
pip3 install tensorflow

以上で、インストールは完了。

インストールした環境を試してみる

$ python3
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))

Hello, TensorFlow!

「Hello, TensorFlow!」の行が表示されれば、正常にインストールできている。

おわりに

インストールは簡単でしたが、実際に使うのは難しそう。
もう少し勉強してから追記するね。(^^ゞ

関連ページ

Raspicastのメモ

(2021-02-06 初稿 - 2021-05-19 転記・修正)

 

GoogleのChromecast(たぶん第3世代)を購入。

設定をしていたら、Chromecastと似たようなことが、Raspberry Pi4でもできることがわかり、設定したときのメモ。

 

【設定時の環境】

$ cat /etc/debian_version

10.7

$ uname -a

Linux somepc 5.10.11-v7l+ #1399 SMP Thu Jan 28 12:09:48 GMT 2021 armv7l GNU/Linux

 

【ラズパイの設定】

  • sudo raspi-config で ssh を起動に
  • HDMIでテレビやモニタに正常に映るように
  • omxplayerで再生(ラズパイにはデフォルトでインストールされている)
  • 静止画を見るためには別のソフトをインストールする必要がある

【Andorid端末の設定】
Google Playストアから raspberrycast をダンロード、インストール

Hostname or IP 192.168.xxx.xxx

Port 22

Username pi

Password **********

なかなかうまく行かないときがあるが、何度もチャレンジしていると動作する

インストールしたスマホとラズパイが同じネットワーク(WiFi)を使っていることを確認

 

筆者がうまくできているサイト

アプリケーションを実行後、「共有」を選択

  • 「Cast(Raspicast)」を選択すれば、すぐに再生
  • 「Queue(Raspicast)」を選択で、キューに保存

 

f:id:hymd3a:20210519160447j:plain

Android側の設定

f:id:hymd3a:20210519160526j:plain

Youtubeを再生中のRaspicast

 

関連ページ

Thinkpad E430に Debian 10

(2020-07-12 初稿 - 2020-05-19 転記・修正)

はじめに

先日、息子が帰って来て、不要になった古いノートPCを置いて行った。息子にとっては古いものでも、筆者にとってはまだまだ十分使えるノートPCだった。このページは、Thinkpad E430にDebian 10(Buster)をインストールしたときのメモ。

Thinkpad E430 のスペック

[CPU] intel Core I-5 3230M

[Memory] 4GB

[Harddisk] HDD 500GB ⇒ 230GB SSDに換装

[GraphicBoard] CPU内蔵 Intel® HDD Graphics 4000

[Display] LED14.0型HD TFT液晶 (1,366×768ドット)、光沢なし

[OS] Windows 8 (64bit) ⇒ Windows 10にアップグレード 

詳しくは、以下のLenovoサイトを参照のこと。

Debian 10(Buster)のインストール

Debian Busterからは、UEFIに対応しているが、BIOSを確認したところ、息子はWindows 8、10ともにLEGCYモードで利用していたもよう。

DebianでもBIOSUEFIをLEGCYモードのまま利用した。

インストールした時期が2020年7月なので、Debianのバージョンは10.4で、いつものようにnetinst版をダウンロードしてインストールした。

  • deian-10.4.0-amd64-netinst.iso

デスクトップ環境は筆者お好みのLXDEにし、インストールは順調に終った。

インストール後の設定

これ以降は、主にノートPCで必要なことのみを記載し、テスクトップ、ノートPC共有のことは、以下のサイトを参照のこと。超手抜き (-_-;)

バッテリー残量の表示

ノートPCなので、バッテリー残量が確認できると便利。

おおよその残量は、acpiをインストールして、以下のコマンドで確認できる。

$ sudo apt install acpi
$ acpi -bi
Battery 0: Full, 100%
Battery 0: design capacity 3195 mAh, last full capacity 3394 mAh = 100%

acpitoolやupowerコマンドをインストールするともう少し詳しい情報が得られる。

 

また、パッケージをインストールしなくても、以下のファイルをcatすることで、詳しい情報を得ることができる。

$ cat /sys/class/power_supply/BAT0/uevent
POWER_SUPPLY_NAME=BAT0
POWER_SUPPLY_STATUS=Full
POWER_SUPPLY_PRESENT=1
POWER_SUPPLY_TECHNOLOGY=Li-ion
POWER_SUPPLY_CYCLE_COUNT=0
POWER_SUPPLY_VOLTAGE_MIN_DESIGN=10800000
POWER_SUPPLY_VOLTAGE_NOW=12164000
POWER_SUPPLY_POWER_NOW=0
POWER_SUPPLY_ENERGY_FULL_DESIGN=38880000
POWER_SUPPLY_ENERGY_FULL=41300000
POWER_SUPPLY_ENERGY_NOW=41300000
POWER_SUPPLY_CAPACITY=100
POWER_SUPPLY_CAPACITY_LEVEL=Full
POWER_SUPPLY_MODEL_NAME=45N1043
POWER_SUPPLY_MANUFACTURER=SANYO
POWER_SUPPLY_SERIAL_NUMBER= 4745

お好きな方を。

 

スピーカー音量の調整

スピーカーの音量の調節は、「Fn+F3」(FnキーとF3キーを同時に押す)で、音量アップ。「Fn+F2」で、音量ダウン。

ディスプレイ輝度の調整

ディスプレイの輝度は、「Fn+F8」や「Fn+F9」で、を調整できなかった。

以前作った brt というshellスクリプトで、輝度調整を行うことができる。

必要な方は、ご覧になって。

おわりに

ThinkPadのキーボードは、キーストロークがノートPCの中では深く、また、キートップの感触も柔らかい感じなので、長時間のキー入力でも快適。

筆者の好みとしては、できれば、左下のFnキーとCtrlキーが逆の配列ならば、なお良かったなぁ。まあ、変更できるけど。

また、何か気がついたら追記する。

関連ページ

 

pythonでTwitter APIを使う

(2020-09-13 初稿 - 2021-06-13 追記)

 

筆者は、twitterライフログツールとして使おうと考えており、rubyのtwを利用させてもらってきた。しかし、twは1日のつぶやきの取得数が20までで、20以上になるとつぶやきが取得できなくなってしまう。

そこで、以下にシンプルに自分のつぶやきをpythonで取得する方法を記述する。

 

なお、詳細は以下を参照のこと。多謝。m(__)m

python-twitterの各APIの詳細】

【user_timelineリクエストのレスポンスの詳細】

Twitter API利用申請の詳細】

スクリプトの作成よりも、twitterAPI利用申請やAPIキー等の取得の方が、手間が掛かった。(-_-;)

筆者は、最初にreadオンリーのスクリプトを登録してしまい、Read + Writeに変更したら、再度Authentication Tokensを取得し直すハメになった。とほほ(-_-;)

ので、登録は、最初からRead+Writeの申請にしておいた方が良いと思う。

 

作成したスクリプトは以下のとおり。

cat p-tweet.py

#!/usr/bin/env python3
#coding: UTF-8

# メッセージを取得する
# ver0.01 2019-05-23 start
# ver0.02 2020-09-13 screen_nameの投稿を取得

import twitter
import oauthlib
import json
import sys
import time, calendar

#  Constant
NAME = 'hymd3a'   # 自分のアカウント名に変更
MAX_COUNT = 50    # 取得したいツィート数 max 200

def DateTime(created_at):
    time_utc = time.strptime(created_at, '%a %b %d %H:%M:%S +0000 %Y')
    unix_time = calendar.timegm(time_utc)
    time_local = time.localtime(unix_time)
    return time.strftime("[%Y-%m-%d %H:%M:%S]", time_local)

# 取得したキーとアクセストークンを設定する
# 以下の * は、自分のコードに変更してね
api = twitter.Api(consumer_key="*************************",
    consumer_secret="**************************************************",
    access_token_key="**************************************************",
    access_token_secret="*********************************************")

# メッセージの取得
timeline = api.GetUserTimeline(screen_name=NAME,count=MAX_COUNT)
for tweet in reversed(timeline):
    print(DateTime(tweet.created_at))
    print(tweet.text)
    print("Retweet: ", tweet.retweet_count)
    print("Favorite: ", tweet.favorite_count)

【出力例】

[2020-09-13 13:15:06]
現在時刻は 2020-09-13 13:15:01 です。「ひゃまだ家」の部屋の 温度= 29.0 ℃, 湿度= 85.3 %, 飽差(HD)= 4.23 g/m3 で、Raspberry Pi3のCPU温度は temp=53.7'C です。(^^♪
Retweet:  0
Favorite:  3

APIにオプションがたくさんあるのに対し、アプリケーションの登録が面倒なので、投稿や取得、検索等の機能を盛り込んで行くと、かなり複雑なオプションを持つスクリプトになりそう。

また、機能追加等したら追記する。

(2021-06-13 追記)

TwitterAPIもVer2がでて、「いいね」をしてくれたユーザIDがわかったりと便利な機能も追加された。

以下に、Ver2を使った簡単なスクリプトを作成したので、参考までに。

関連ページ

pythonでホームディレクトリを確認

(2020-09-13 初稿 - 2021-05-19 転記・修正)

 

pythonで各ユーザのホームディレクトリの取得方法を以下に記す。

以下のページに簡単に取得する方法の記述があった。多謝

スクリプトは以下のとおり。

import os

print os.environ['HOME']

関連ページ