fc2ブログ

Ubuntuの.NET CoreでGUIを使う(2)

 前回の続きで、QMLファイルを使って.NET Coreに実装する内容をお話します。
 前回、Qt Creatorで作成したQMLファイルは以下の通りです。テキストボックスやボタン等のコントロールのプロパティとクリックイベント(37、49行目)が記録されています。
# Qt_test.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.15

Window {
id: window
visible: true
width: 320
height: 240
title: qsTr("Hello World")

Text {
id: element
x: 44
y: 117
width: 229
height: 81
color: "#ff0000"
layer.smooth: false
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
fontSizeMode: Text.FixedSize
clip: false
textFormat: Text.PlainText
font.pixelSize: 20
}

Button {
id: button1
x: 44
y: 44
text: qsTr("Show")

Connections {
target: button1
onClicked: element.text= "Hello world!"
}
}

Button {
id: button2
x: 178
y: 44
text: qsTr("Clear")

Connections {
target: button2
onClicked: element.text = ""
}
}
}
 まず、Ubuntuのターミナルから以下のコマンドでプロジェクト(プロジェクト名:QtTest)を作成しました。
dotnet new console -o QtTest
次に、作業フォルダに移動して、
cd QtTest
QML.NETライブラリをインストールしました。
dotnet add package Qml.Net.LinuxBinaries
この際、追加ライブラリのインストール要求がありましたので、以下のコマンドを実行。
sudo apt install libc6-dev
 次に、メインプログラムのProgram.csを以下の通り編集しました。18行目でQMLファイルを読み込んでいます。
// Program.cs
using System;
using Qml.Net;
using Qml.Net.Runtimes;

namespace QtTest
{
class Program
{
static int Main(string[] args)
{
RuntimeManager.DiscoverOrDownloadSuitableQtRuntime();

using (var app = new QGuiApplication(args))
{
using (var engine = new QQmlApplicationEngine())
{
engine.Load("Qt_test.qml");
return app.Exec();
}
}
}
}
}
コード作成後、以下のコマンドでビルドしました。
dotnet build
正常にビルドできれば、実行ファイル(dllファイル)は「bin/Debug/netcoreapp3.1」フォルダに作成されますので、移動して実行しました。
cd bin/Debug/netcoreapp3.1
dotnet ./QtTest.dll
この時、QMLファイル(Qt_test.qml)はdllファイルと同じフォルダ、つまり「bin/Debug/netcoreapp3.1」に入れておきます。実行すると、無事にフォームが立ち上がりました。dotNet_gui1_200823.pngShowボタンを押すと「Hello world!」が表示され、Clearボタンで表示が消去されることも確認できました。dotNet_gui2_200823.png Ubuntuの.NET Core(C#)でもQtを利用することで気軽にGUIが使えることが分かりました。また、別案件で使ってみようと思います。
スポンサーサイト



Ubuntuの.NET CoreでGUIを使う(1)

 自宅では全くWindowsマシンを使わないのですが、Ubuntu、MacOSマシンでC#を使いたくて「.NET Core」を触ることがたまにあります。この時、GUIが利用できないので、Consoleアプリでプログラミングすることがほとんどです。.NET5以降でどうなるかは分かりませんが、現在(2020年8月時点)、最新の.NET Core3.1でGUI(Windows Form)をサポートしているのはWindowsOSのみという認識です。

 そのような状況で、.NET Core(Ubuntu, MacOS)でもGUIを簡便に使える方法がないかとネットで探していたら、「Qml.NET」なるものを見つけました。中身はGUIツールキットとして有名なQt(キュート)を使う方法です。昔PythonでPyQtを使ったことがありますが、今回はそれをC#(.NET)で使う試みです。早速、興味本位で試してみました。

 まず、準備として、Qt Creatorをインストールする必要がありました。Qt Creatorは昔々MacBook Airに入れておいたのですが、ディスク容量が半端ないぐらい多かったので、削除したことを思い出しました。今回、MacBook Airで再インストールを試みていたのですが、途中で「Xcodeのインストール云々」と言われてきたので、取りやめることにしました。Xcodeも以前MacBook Airに入れていたのですが、これも容量が多く、全く使わないので削除していました。結局、MacOSは取りやめて、Ubuntuマシンで行うことにしました。
 
 インストールはQtのダウンロードサイトから無償の「Downloads for open source users」から「Go open source」を選び、QtCreator_install1_200816.png「Get Started」の「Download the Qt Online Installer」を選びました。QtCreator_install2_200816.pngダウンロードされたファイルは「qt-unified-linux-x64-3.2.3-online.run」で、その後の処理は参考サイトの通り進めました。インストールの所要時間は1時間程度でひたすら待ちました・・。

 インストールも無事に終わり、アプリの中から「Qt Creator」を選択し、起動。QtCreator1_200816.png以後、GUIを作成するプロセスに入りますが、「Qml.NET」ではQML(Qt Modeling Language)ファイルが必要になりますので、その作成の流れを説明します。
1.「プロジェクト」-「+ New」を選択QtCreator2_200816.png2.新しいプロジェクトで「他のプロジェクト」-「Qt Quick UI Prototype」を選び、「選択ボタン」を押す。QtCreator3_200816.png3.プロジェクトパスの名前にプロジェクト名を入力し、次へQtCreator4_200816.png4.プロジェクトの詳細定義は、「デフォルト(Qt 5.12)」のままで次へQtCreator5_200816.png5.キットの選択も「デフォルトのまま」で次へQtCreator6_200816.png6.プロジェクトの管理で「完了ボタン」を押すとQtCreator7_200816.png「編集」画面が立ち上がりました。QMLファイルのコードを記入するウィンドウです。QtCreator8_200816.png「デザイン」に切り替えると、Visual Studioに似たフォームエディタやフォームの部品となるエレメントが表示されました。QtCreator9_200816.png確認すると、フォームの部品(Control)が表示されていなかったので、「QML Imports」から「インポートを追加」し、「QtQuick.Controls 2.15」を導入しました。QtCreator10_200816.png表示を「エレメント」に戻すとFormのControlが追加されたことが分かりました。QtCreator11_200816.pngFormにbuttonを2つ(button1、button2)、テキストボックス(element)を設置し、button1(Show)を押すと「Hello world!」をテキストボックスに表示し、button2(Clear)を押すと表示を消すようにしました。イベントハンドラは、右下の「ターゲット - シングルハンドラ - アクション」部分に記載することで、QMLファイルにも反映されました。左下の「実行」ボタンを押すことで、作成したフォーム(GUI)の動作確認も出来ました。QtCreator12_200816.png
次回は作成したQMLファイルを使って.NET Coreに実装する内容をお話します。

Raspi4のJupyter Notebookをリモートで使う

 Jupyter Notebookで行っているデータ解析を普段利用しているPCでなく、他のPCで行い、かつリモート操作したいと、ふと思いました。いつもの思いつきです。リモートデスクトップでも良いのですが、普段使いPCの負荷をより少なく、簡便にできる方法を探していた所、ブラウザ経由でできることが分かりました。早速試してみました。
 ネット上に情報がありましたので、それを参考に進めました。進めている中で、Raspi4のJupyter Notebookが正常に動作しないことが分かり、結局、SDカードにRaspi4を入れ直し(クリーンインストールし)ました。クリーンインストール後にJupyter Notebookは正常に動きました。原因はよく分かっていませんが、インストールされていたPython環境と喧嘩していた?のかもしれません・・。

 仕切り直して、まず以下のコマンドをRaspi4(IPアドレス:192.168.11.12)のターミナル上で実行させました。
jupyter-notebook --generate-config
jupyter-notebook password
cd ~/.jupyter
.jupyterフォルダには以下の2つのファイルが作成されていました。
jupyter_notebook_config.py
jupyter_notebook_config.json
pyファイルは各種設定、jsonファイルはハッシュ化されたパスワードが記載されたものです。pyファイルの次の項目について、以下の通り設定しました。
c.NotebookApp.ip = '192.168.11.12'
c.NotebookApp.open_browser = False
c.NotebookApp.password = 'sha1:・・(jupyter_notebook_config.jsonに記載のパスワード)・・'
c.NotebookApp.port = 8889
c.NotebookApp.token = u''
 設定も終わったので、Raspi4のターミナルから「jupyter-notebook」を実行しますが、Jupyter-Notebookのブラウザがダイレクトに立ち上がらなくなりました。そこで、ブラウザを立ち上げて「http://192.168.11.12:8889」にアクセスしてみると、パスワード認証画面が表示されました。JupyterNotebookSetting_200810.pngパスワード認証すると、Jupyter Notebookが無事に立ち上がり、コードも正常動作確認できました。JupyterNotebookRun_Raspi_200810.png
 Raspi4側の準備も出来ましたので、いよいよMacbook AirからリモートでRaspi4を操作します。Macのターミナルから以下のコマンドを実行し、
ssh pi@192.168.11.12
jupyter-notebook
ブラウザ(Safari)から以下のURLにアクセスすると、
http://192.168.11.12:8889
パスワード認証画面が表示されましたので、Mac_Remote1_200810.pngパスワードを入力すると、無事にJupyter Notebookが起動しました。Mac_Remote2_200810.png動作確認もOKでした。Mac_Remote3_200810.png
 今回は普段使いのMacBook AirからRaspi4のJupyter Notebookを操作する試みですが、普段使いのPCよりもハイスペックのPCやサーバが相手だとデータ解析もサクサクとできて大変重宝ですね。

RからPythonへの道(18)

 今回は教師なし学習の「17. クラスタリング」のお話です。今回も教材は『データサイエンス教本』を参考にしました。 まず、Pythonのコードです。読み込むデータ(cluster_data.csv)は参考書籍のコードを実行して生成したものを使いました。生データをグラフ描画しても分かるように、クラスタ数は3でk-means法を用いてクラスタリングしました。また、最適なクラスタ数を判断するために、Elbow法の結果をプロットしました。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

# Load data
X = np.loadtxt('cluster_data.csv', delimiter=',')
plt.scatter(X[:,0], X[:, 1], color='black')
plt.show()

# Clustering
kmeans = KMeans(init='random', n_clusters=3)
kmeans.fit(X)
y_pred = kmeans.predict(X)

merge_data = pd.concat([pd.DataFrame(X[:,0]), pd.DataFrame(X[:,1]), pd.DataFrame(y_pred)], axis=1)
merge_data.columns = ['feature1', 'feature2', 'cluster']

ax = None
colors = ['blue', 'red', 'green']
for i, data in merge_data.groupby('cluster'):
ax = data.plot.scatter(x='feature1', y='feature2', color=colors[i], label=f'cluster{i}', ax=ax)
plt.show()

# Elbow method plot
dist_list = []
for i in range(1,10):
kmeans = KMeans(n_clusters=i, init='random', random_state=0)
kmeans.fit(X)
dist_list.append(kmeans.inertia_)

plt.plot(range(1,10), dist_list, marker='+')
plt.xlabel('Number of cluster')
plt.ylabel('Distortion')
plt.show()
print(dist_list)
実行結果は以下の通りです。
9行目の生データプロットの結果Python_Clustering1_200810.png23行目のクラスタリングの結果Python_Clustering2_200810.png35行目のElbowプロットの結果Python_Clustering3_200810.pngクラスター数3の位置にElbow(肘)がありますので、クラスター数=3は妥当と言えます。
Elbowプロットの値
[4372.460950204313, 976.8773336900748, 186.3658862010145, 154.0785631890923, 130.02425208172428, 116.05569958229077, 98.78215673557868, 85.37059657683321, 81.36184950627461]

 次にRのコードです。Rでクラスタリング(k-means法)を利用するにはclusterというパッケージのkmeans関数を使います。
library(cluster)

# Load data
X = read.csv('cluster_data.csv', header = FALSE)
plot(X, type="p", pch=16, xlim=c(-3, 8), ylim=c(-12.5,7.5))

# Clustering
kmeans_r = kmeans(X, 3)
clusplot(X, kmeans_r$cluster, color=TRUE, shade=TRUE, lines=0)

plot(X[kmeans_r$cluster==1,], type="p", pch=16, col="red", xlim=c(-3, 8), ylim=c(-12.5,7.5))
par(new=T)
plot(X[kmeans_r$cluster==2,], type="p", pch=16, col="green", xlim=c(-3, 8), ylim=c(-12.5,7.5))
par(new=T)
plot(X[kmeans_r$cluster==3,], type="p", pch=16, col="blue", xlim=c(-3, 8), ylim=c(-12.5,7.5))

# Elbow method plot
xx = array(0, dim=c(10))
for (i in 1:10) {
kmeans_temp = kmeans(X, i)
xx[i] = kmeans_temp$tot.withinss
}
plot(1:10, xx, type="b", pch=16, xlab = "Number of clusters", ylab = "Distortion")
print(xx)
実行結果は以下の通りです。
5行目の生データプロットの結果R_Clustering1_200810.png9行目のクラスタリングの結果R_Clustering2_200810.pngPythonの結果グラフと異なることに気付きましたが、これはRの方でPCA(主成分分析)をした後の結果グラフのためです。Pythonと同じグラフを得るためには、11〜15行目を実行させました。結果グラフは以下の通りで、Pythonグラフを同じです。R_Clustering3_200810.png23行目のElbowプロットの結果R_Clustering4_200810.pngPythonと同じく、クラスタ数=3が最適であることが分かりました。
Elbowプロットの値
> print(xx)
[1] 4372.46095 976.87733 186.36589 163.21634 129.98389 114.96342 108.93539 90.46456
[9] 80.07354 110.97344
 今回、PythonとRでクラスタリングを行いました。今回の事例のような線形的なクラスタリングは簡単に行えることが良く分かりました。複雑なモデルはまたの機会にして、次回は「主成分分析」の話をしたいと思います。

『RからPythonへの道』バックナンバー
(1) はじめに
(2) 0. 実行環境(作業環境)
(3) 1. PythonからRを使う方法 2. RからPythonを使う方法
(4) 3. データフレーム
(5) 4. ggplot
(6) 5.行列
(7) 6.基本統計量
(8) 7. 回帰分析(単回帰)
(9) 8. 回帰分析(重回帰)
(10) 9. 回帰分析(ロジスティック回帰1)
(11) 10. 回帰分析(ロジスティック回帰2)
(12) 11. 回帰分析(リッジ、ラッソ回帰)
(13) 12. 回帰分析(多項式回帰)
(14) 13. 決定木(分類)(1)
(15) 14. 決定木(分類)(2)
(16) 15. k近傍法(kNN)
(17) 16. サポートベクターマシン(SVM)

RからPythonへの道(17)

 前回から2ヶ月近く経ってしまいました。今回は「16. サポートベクターマシン(SVM)」を用いて「分類」をしていきたいと思います。今回も教材は『データサイエンス教本』です。 まず、Pythonのコードですが、教材のコードに一部追記をしています。データセットは前回のbreast_cancerを用いました。30個の説明変数から癌であるか否かを分類予測するものです。変数の値をそのまま処理したものと正規化(標準化)したものでTrain/Testしました。
from sklearn.svm import LinearSVC
from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix

cancer = load_breast_cancer()

X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, stratify = cancer.target, random_state=0)

# SVM(classification)
model = LinearSVC()
model.fit(X_train, y_train)
print(model.coef_)
print(model.intercept_)

# train
print(confusion_matrix(y_train, model.predict(X_train)))
print('accuracy(train):{:.3f}'.format(model.score(X_train, y_train)))

# test
print(confusion_matrix(y_test, model.predict(X_test)))
print('accuracy(test):{:.3f}'.format(model.score(X_test, y_test)))

# Standardization
sc = StandardScaler()
sc.fit(X_train)
X_train_std = sc.transform(X_train)
X_test_std = sc.transform(X_test)

model.fit(X_train_std, y_train)

# train
print(confusion_matrix(y_train, model.predict(X_train_std)))
print('accuracy(train):{:.3f}'.format(model.score(X_train_std, y_train)))

# test
print(confusion_matrix(y_test, model.predict(X_test_std)))
print('accuracy(test):{:.3f}'.format(model.score(X_test_std, y_test)))
実行結果は以下の通りです。
[[ 3.08392384e-02  2.14937156e-02  7.84780923e-02  5.58287920e-04
-6.30953585e-04 -3.59159724e-03 -4.89102197e-03 -2.00541581e-03
-6.42593859e-04 -1.73715344e-04 6.52002496e-04 2.26475648e-03
-4.02754550e-03 -5.41561872e-03 -4.55159689e-05 -9.42294282e-04
-1.16761810e-03 -2.79803930e-04 -1.89495180e-04 -8.58443283e-05
2.93326518e-02 -3.68825133e-02 -1.39322042e-02 -7.34031390e-03
-1.17121002e-03 -1.27290098e-02 -1.48126398e-02 -4.30424424e-03
-2.66434274e-03 -1.09637200e-03]]
[0.00513864]

[[131 28]
[ 3 264]]
accuracy(train):0.927
[[45 8]
[ 2 88]]
accuracy(test):0.930

[[156 3]
[ 0 267]]
accuracy(train):0.993
[[51 2]
[ 5 85]]
accuracy(test):0.951
 教材ではTrainデータを用いて分類学習した結果をTestデータに当てはめて分類予測し、その正解率を評価しているのですが、Trainデータの正解率の0.927(92.7%)がどのように算出されたのか、よく分からなかったので、Trainデータで得られた係数と切片を出力して、手計算で調査しました。SVM_calc_process_200801.png目的変数は0か1の2値です。Trainデータの各レコードについて、フィールド毎に得られた係数を掛け合わせていきました。重回帰分析の予測値を求める要領です。
つまり、0番目のデータについては以下の通りです。
 12.3100×0.0308 + 16.5200×0.0215 + ・・・ +0.0761×(-0.0011) + 0.0051 = 0.9218

各レコードを計算していくと正や負の値が得られました。ここで、線形分離で2つに分類する上で、正側を1、負側を0とし、それを判定値(Judge)としました。次に、真値と比較して、正しく判定されたものを1、誤って判定されたものを0として、JudgeOK欄に記入しました。
全データは426データで正しく判定されたデータ数は395だったので、395/426=0.927で正解率と一致することが分かりました。

 また、結果の11、12行目の混同行列(Confusion Matrix)は以下の行列で、正解率は(TN+TP)/(TN+FP+FN+TP)で表されるので、
0 = Negative, 1 = Positive

Predicted
0 1
Actual 0 TN FP
1 FN TP
 (131+264)/(131+28+3+264) = 395/426 = 0.927で、これもPythonのコードのscoreと同じ値になりました。

 次にRのコードです。RでSVMを利用するにはkernlabというパッケージのksvm関数を使いました。
library(dplyr) 
library(kernlab)

# data read
url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data'
cancer_temp = read.csv(url, header = FALSE)
cancer = cancer_temp[,3:32]
cancer.target = as.data.frame(ifelse(cancer_temp[,2] == 'B', 1, 0))
df = data.frame(cancer, cancer.target)
names(df)[31] = "cancer.target"
head(df)

set.seed(1)
train.rate = 0.7 # training data rate
train.index = sample(nrow(df),nrow(df) * train.rate)
df_Train = df[train.index,]
df_Test = df[-train.index,]

# SVM(classification)
df.ksvm = ksvm(df_Train$cancer.target~ ., data = df_Train, type="C-svc", C=1)
print(df.ksvm)

# train
train_svm = predict(df.ksvm , df_Train[,1:30])
train_svm_res = ifelse(train_svm>0, 1, 0)
result.train = table(train_svm_res , df_Train[,31])
print(result.train)
print(sum(train_svm_res==df_Train[,31])/length(train_svm_res))

# test(predict)
pred_svm = predict(df.ksvm, df_Test[,1:30])
pred_svm_res = ifelse(pred_svm>0, 1, 0)
result.pred = table(pred_svm_res , df_Test[,31])
print(result.pred)
print(sum(pred_svm_res==df_Test[,31])/length(pred_svm_res))
実行結果は以下の通りです。
Support Vector Machine object of class "ksvm" 

SV type: C-svc (classification)
parameter : cost C = 1

Gaussian Radial Basis kernel function.
Hyperparameter : sigma = 0.047963926058988

Number of Support Vectors : 107

Objective Function Value : -43.704
Training error : 0.012563

train_svm_res 0 1
0 139 0
1 5 254
[1] 0.9874372

pred_svm_res 0 1
0 65 2
1 3 101
[1] 0.9707602
Pythonの正規化した時と同等の結果が得られました。
 SVMはあまり利用したことがない解析手法だったので、大変勉強になりました。次回は、教師なし学習のクラスタリングのお話をします。

『RからPythonへの道』バックナンバー
(1) はじめに
(2) 0. 実行環境(作業環境)
(3) 1. PythonからRを使う方法 2. RからPythonを使う方法
(4) 3. データフレーム
(5) 4. ggplot
(6) 5.行列
(7) 6.基本統計量
(8) 7. 回帰分析(単回帰)
(9) 8. 回帰分析(重回帰)
(10) 9. 回帰分析(ロジスティック回帰1)
(11) 10. 回帰分析(ロジスティック回帰2)
(12) 11. 回帰分析(リッジ、ラッソ回帰)
(13) 12. 回帰分析(多項式回帰)
(14) 13. 決定木(分類)(1)
(15) 14. 決定木(分類)(2)
(16) 15. k近傍法(kNN)

ご訪問者数

(Since 24 July, 2016)

タグクラウド


プロフィール

Dr.BobT

Author: Dr.BobT
興味のおもむくままに生涯考え続けるエンジニアでありたい。

月別アーカイブ

メールフォーム

名前:
メール:
件名:
本文: