在 C# 中使用 Keras


轉自:https://moefactory.com/3004.moe

 

在 C# 中使用 Keras

2019 年 10 月 05 日 2

折騰萬歲!

是什么?怎么用?

前段時間突然有了在 C# 中調用 Keras 生成的模型的奇怪需求,本來想的是干脆直接調用 Python 腳本然后傳個參進去,但是這樣不是很好玩,Google 了一下發現竟然有人把 Python 的幾個機器學習框架都移植到了 C# 下,而 Keras.NET 就是其中一個,這下就很有趣了。

Keras.NET 是 SciSharp(是不是很熟悉,Python 中的科學計算工具包叫做 SciPy,不過我不知道這兩者是不是一家)移植的用於 .NET 的 Keras 框架,其最大的特點是盡量使 C# 中的語法與 Python 原版的相似。另外 SciSharp 還移植了 NumSharp 等一系列庫。關於Keras.NET,可參見:https://www.cnblogs.com/cgzl/p/11145382.html

基於上述特點,Python 代碼只需做極少量的改動就能在 C# 中使用,如下所示( 代碼摘自 Github 官方 repo)。

這是 Python 代碼:

batch_size = 128
num_classes = 10
epochs = 12
 
# input image dimensions
img_rows, img_cols = 28, 28
 
# the data, split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()
 
if K.image_data_format() == 'channels_first':
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)
else:
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)
 
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
 
# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
 
model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3),
activation='relu',
input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
 
model.compile(loss=keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adadelta(),
metrics=['accuracy'])
 
model.fit(x_train, y_train,
batch_size=batch_size,
epochs=epochs,
verbose=1,
validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

這是 C# 代碼:

int batch_size = 128;
int num_classes = 10;
int epochs = 12;
 
// input image dimensions
int img_rows = 28, img_cols = 28;
 
Shape input_shape = null;
 
// the data, split between train and test sets
var ((x_train, y_train), (x_test, y_test)) = MNIST.LoadData();
 
if(Backend.ImageDataFormat() == "channels_first")
{
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);
}
else
{
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);
}
 
x_train = x_train.astype(np.float32);
x_test = x_test.astype(np.float32);
x_train /= 255;
x_test /= 255;
Console.WriteLine($"x_train shape: {x_train.shape}");
Console.WriteLine($"{x_train.shape[0]} train samples");
Console.WriteLine($"{x_test.shape[0]} test samples");
 
// convert class vectors to binary class matrices
y_train = Util.ToCategorical(y_train, num_classes);
y_test = Util.ToCategorical(y_test, num_classes);
 
// Build CNN model
var model = new Sequential();
model.Add(new Conv2D(32, kernel_size: (3, 3).ToTuple(),
activation: "relu",
input_shape: input_shape));
model.Add(new Conv2D(64, (3, 3).ToTuple(), activation: "relu"));
model.Add(new MaxPooling2D(pool_size: (2, 2).ToTuple()));
model.Add(new Dropout(0.25));
model.Add(new Flatten());
model.Add(new Dense(128, activation: "relu"));
model.Add(new Dropout(0.5));
model.Add(new Dense(num_classes, activation: "softmax"));
 
model.Compile(loss: "categorical_crossentropy",
optimizer: new Adadelta(), metrics: new string[] { "accuracy" });
 
model.Fit(x_train, y_train,
batch_size: batch_size,
epochs: epochs,
verbose: 1,
validation_data: new NDarray[] { x_test, y_test });
var score = model.Evaluate(x_test, y_test, verbose: 0);
Console.WriteLine($"Test loss: {score[0]}");
Console.WriteLine($"Test accuracy: {score[1]}");

一些小問題

slice的替代

目前我用到的功能中和 Python 有點區別的是,盡管 NumSharp 通過字符串作為參數實現了 Python 中的slice功能,但是並不能像 Python 里一樣使用x = x[:, :, :, np.newaxis]。不過好在就算是 Python,也有另一種方法完成同樣的功能,就是np.expand_dims,對應在 C# 里則是x = np.expand_dims(x, 3);

系統架構

需要注意的是,使用 Keras.NET 需要安裝版本匹配的 Python,並且僅支持 64 位的 Windows,這也就意味着編譯 .NET 程序時不能選擇“首選 32 位”。

.NET Core 上的注意事項

最開始我的程序是用 WinForms (.NET Framework 4) 寫的,跑起來沒什么大問題,就是只能依附於 Visual Studio 的調試模式。后來在微軟發布 .NET Core 3 並支持 WPF 之后,九月份遷移項目的時候就出現了問題,一直報一個奇怪的錯誤,給作者提了 issue 之后也沒什么太大的進展。

這學期期末的時候又想起來這個事情,試了一下在 .NET Core 3 下的 Console App 里跑起來是沒問題的。於是再次提了一個 issue。最終得知是因為一些 Python 模塊會使用sys.strerrsys.stdout來輸出一些東西,而 WinForms 和 WPF 是沒有控制台的——大概這也是之前不能脫離 Visual Studio 運行的一個問題吧。

解決辦法是更新到最新的 Keras.NET 並使用Keras.Keras.DisablePySysConsoleLog = true來禁用這些輸出。如果有獲取這些輸出的需求,可以使用以下代碼:

string stdout = Keras.Keras.GetStdOut();
string stderr = Keras.Keras.GetStdError();


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM