Code Planet

为了发现更大的世界


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

Keras模型C++部署最强指南

发表于 2019-07-29 | 分类于 C++

I.Keras保存模型

1
model.save("unet.h5")

II.Keras模型转换TF模型

1
2
3
python keras_to_tensorflow.py 
--input_model="path/to/keras/model.h5"
--output_model="path/to/save/model.pb"

可能遇到的问题是找不到自定义loss,查看相关issues可以找到解决方案。

III.TensorBoard查看模型

1
2
3
4
5
6
7
8
import tensorflow as tf

model = 'model.pb'
graph = tf.get_default_graph()
graph_def = graph.as_graph_def()
graph_def.ParseFromString(tf.gfile.FastGFile(model, 'rb').read())
tf.import_graph_def(graph_def, name='graph')
summaryWriter = tf.summary.FileWriter('log/', graph)

然后使用TB查看输入与输出

1
tensorboard --logdir ./log --host 127.0.0.1 --port 4321

可以查看到输入类似这样,这个层的名字C++调用的的时候需要完全匹配
Alt text

IV.C++调用TF模型进行前向计算

  1. 点此下载合适的TF预编译DLL(不必与Python训练版本一致),注意不要自行编译,费时又费力坑又多。
  2. 输入参数转换为Tensor

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    Tensor Image2Tensor(Image& input)
    {
    // 创建tensor
    tensorflow::Tensor image_input = tensorflow::Tensor(tensorflow::DT_FLOAT,
    tensorflow::TensorShape({ 1, input->GetDimensions()[0], input->GetDimensions()[1], 1 }));
    float *tensor_data_ptr = image_input.flat<float>().data();

    for (int y = 0; y < dims[1]; y++)
    {
    for (int x = 0; x < dims[0]; x++)
    {
    *tensor_data_ptr = input[x][y]/255.0;
    tensor_data_ptr++;
    }
    }
    return image_input;
    }
  3. TF载入模型前向计算

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    Session* session;
    Status status = NewSession(SessionOptions(), &session); // 创建新会话Session

    string model_path = "model.pb"; // 保存的模型路径
    GraphDef graphdef; // 当前模型的图定义
    Status status_load = ReadBinaryProto(Env::Default(), model_path, &graphdef); // 从pb文件中读取图模型;
    if (!status_load.ok())
    {
    std::cout << "ERROR: Loading model failed..." << model_path << std::endl;
    std::cout << status_load.ToString() << "\n";
    return -1;
    }

    Status status_create = session->Create(graphdef); // 将图模型导入会话Session中;
    if (!status_create.ok())
    {
    std::cout << "ERROR: Creating graph in session failed..." << status_create.ToString() << std::endl;
    return -1;
    }
    cout << "Session successfully created." << endl;

    // 开始进行预测
    std::vector<Tensor> resized_tensors; // 用于保存读取的图片的tensor数组


    auto image_tensor = Image2Tensor(image); // 获取输入Tensor
    const Tensor& resized_tensor = image_tensor;
    std::cout << resized_tensor.DebugString() << endl; // 打印出输入模型的tensor形状
    vector<tensorflow::Tensor> outputs;

    string output_node = "conv2d_19/Sigmoid"; // 模型输出层

    // 开始预测,这里的输入名images要和模型的输入相匹配
    Status status_run = session->Run({ { "input_1", resized_tensor } }, { output_node }, {}, &outputs);
    if (!status_run.ok())
    {
    std::cout << "ERROR: RUN failed..." << std::endl;
    std::cout << status_run.ToString() << "\n";
    return -1;
    }

    // 取出输出值
    std::cout << "Output tensor size:" << outputs.size() << std::endl;
    for (std::size_t i = 0; i < outputs.size(); i++)
    {
    std::cout << outputs[i].DebugString() << endl; // 打印出模型输出的tensor的形状
    }

    Tensor result_tensor = outputs[0]; // 取出第一个tensor

详细例程可以参考此链接

C#调用C++编译Dll

发表于 2019-07-29 | 分类于 c++

C++编译DLL

这里以C++编译数组排序为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extern "C" __declspec(dllexport) void array_sort(int* a, int len)
{
for (int i = 0; i < len; i++)
{
for (int j = i + 1; j < len; j++)
{
if (a[i] > a[j])
{
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}
}

注意库的编译方式x86/x64/Debug/Release

C#调用DLL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System.Runtime.InteropServices;
[DllImport("CppDll.dll", EntryPoint = "array_sort")]

static extern int array_sort(int[] a, int b);

private void button1_Click(object sender, EventArgs e)
{
int[] array = new int[5];
array[0] = 10;
array[1] = 12;
array[2] = 11;
array[3] = 15;
array[4] = 13;
array_sort(array,5);

for (int i = 0; i < 5; i++)
{
Console.Write(array[i].ToString()+",");
}
}

同样需要注意库的编译方式x86/x64/Debug/Release,C#中默认为Any CPU

参考:

[1].https://www.codeguru.com/csharp/csharp/cs_data/article.php/c4217/Calling-Unmanaged-Code-Part-1--simple-DLLImport.htm

vtkRenderWindow控件集成

发表于 2019-06-17 | 分类于 vtk

vtk内部实现了vtk与mfc/qt的集成控件,但是如果需要跟wpf集成该如何操作呢?
实现的方法是由wpf传入一个控件的HWND,作为vtkRenderWindow的父窗口。
vtkRenderWindow内部有一个SetParentId方法,可以设置父窗口句柄。
此种方法的局限是,vtkRenderWindow的大小无法自适应窗口大小,需要重写原来窗口的resize方法。
具体实现示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void display(HWND parent,int w,int h)
{
//Create a cone
vtkSmartPointer<vtkConeSource> coneSource =
vtkSmartPointer<vtkConeSource>::New();
coneSource->Update();

//Create a mapper and actor
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(coneSource->GetOutputPort());

vtkSmartPointer<vtkActor> actor =
vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);

//Create a renderer, render window, and interactor
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);

//Add the actors to the scene
renderer->AddActor(actor);
renderer->SetBackground(.3, .2, .1); // Background color dark red
renderWindow->SetSize(w,h);
renderWindow->SetParentId(parent);
renderWindow->Render();
renderWindowInteractor->Start();
}

REF:
[1].https://stackoverflow.com/questions/30301087/vtk-render-into-c-sharp/30301203#30301203

Keras Early Stopping 教程

发表于 2019-05-08 | 分类于 dl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from sklearn.datasets import make_moons
from keras.models import Sequential
from keras.layers import Dense
from keras.callbacks import EarlyStopping
from keras.callbacks import ModelCheckpoint
from matplotlib import pyplot
from keras.models import load_model
# 生成2D分类数据集
X, y = make_moons(n_samples=100, noise=0.2, random_state=1)
# 切割训练/测试集
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# 定义模型
model = Sequential()
model.add(Dense(500, input_dim=2, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# 设置early stop
es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=200) # patience表示在n个epoch上没有提升时停止
mc = ModelCheckpoint('best_model.h5', monitor='val_acc', mode='max', verbose=1, save_best_only=True)
# 训练模型
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, verbose=0, callbacks=[es, mc])
# 加载模型
saved_model = load_model('best_model.h5')
# 评估模型性能
_, train_acc = saved_model.evaluate(trainX, trainy, verbose=0)
_, test_acc = saved_model.evaluate(testX, testy, verbose=0)
print('Train: %.3f, Test: %.3f' % (train_acc, test_acc))

[1].https://machinelearningmastery.com/how-to-stop-training-deep-neural-networks-at-the-right-time-using-early-stopping/

boost库signals2简介

发表于 2019-04-10 | 分类于 c++

bind简介

bind可以用来绑定函数指针、函数引用、成员函数指针和函数对象等的参数。
bind接收第一个参数为可调用对象f,之后bind最多可接受9个参数,且参数数量与f的参数数量一致。

  • 绑定普通函数

    1
    2
    3
    int f(int a,int b){return a+b;}
    bind(f,_1,9)(3); // 固定第二个参数,等价于f(3,9)
    bind(f,9,_1)(3); // 固定第二个参数,等价于f(9,3)
  • 绑定成员函数

    1
    2
    3
    4
    struct demo{void func(int a,int b){return a+b;}};
    demo x;
    bind(&demo::func,x,_1,9)(3);
    bind(&demo::func,x,9,_1)(3);

signals2简介

signals2实现了线程安全的回调机制,一个signal可以关联多个slot,当signal发出时,所有关联的slot会被调用。

实例简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <boost\signals2.hpp>	// signals2头文件
#include <boost\bind.hpp> // bind头文件
#include <iostream>

using namespace std;

struct Object
{
int func(int a, int b)
{
return a + b;
}
};

void main()
{
// 创建实例
Object* obj = new Object;

// 创建signal
// boost::signals2::signal<返回值(参数1,参数2,...)> sig;
boost::signals2::signal<int(int, int)> sig;

// 连接signal
boost::signals2::connection con = sig.connect(boost::bind(&Object::func, obj, _1, _2));

// 获取返回值
cout << *sig(111, 222) << endl;
system("pause");
}

参考:

[1].https://www.boost.org/doc/libs/1_69_0/doc/html/signals2/tutorial.html#id-1.3.37.4.2

台大机器学习2017课程笔记-偏差与方差

发表于 2019-04-04 | 分类于 machine learning

误差推导[1]

假设我们现在有一组训练样本$x_1,…,x_n$,每一个$y_i$有一个$x_i$与之对应。其中的对应关系用方程$y = f(x)+\epsilon $表示,其中$\epsilon $满足条件$\epsilon \sim N(0,\sigma^2 )$,$\hat{f}$为一个函数估计。则平方误差的偏差方差分解所得的推导如下:
首先,对于任意随机变量 $X$,有
\begin{aligned}\operatorname {Var} [X]=\operatorname {E} [X^{2}]-{\Big (}\operatorname {E} [X]{\Big )}^{2}\end{aligned}
整理可得,
\begin{aligned}\operatorname {E} [X^{2}]=\operatorname {Var} [X]+{\Big (}\operatorname {E} [X]{\Big )}^{2}\end{aligned}
由于$f$是确定的,有
\begin{aligned}\operatorname {E} [f]=f\end{aligned}
因此,在$ y=f+\varepsilon$以及$ \operatorname {E} [\varepsilon ]=0$ 的情况下,可以推出$\operatorname {E} [y]=\operatorname {E} [f+\varepsilon ]=\operatorname {E} [f]=f$。
另外,由于 $ \operatorname {Var} [\varepsilon ]=\sigma ^{2}$,故
\begin{aligned}\operatorname {Var} [y]=\operatorname {E} [(y-\operatorname {E} [y])^{2}]=\operatorname {E} [(y-f)^{2}]=\operatorname {E} [(f+\varepsilon -f)^{2}]=\operatorname {E} [\varepsilon ^{2}]=\operatorname {Var} [\varepsilon ]+{\Big (}\operatorname {E} [\varepsilon ]{\Big )}^{2}=\sigma ^{2}\end{aligned}
最后,因为 $\varepsilon $和 ${\hat {f}} $是独立的,所以有
\begin{aligned}\operatorname {E} {\big [}(y-{\hat {f}})^{2}{\big ]}&=\operatorname {E} {\big [}(f+\varepsilon -{\hat {f}})^{2}{\big ]}\\&=\operatorname {E} {\big [}(f+\varepsilon -{\hat {f}}+\operatorname {E} [{\hat {f}}]-\operatorname {E} [{\hat {f}}])^{2}{\big ]}\\&=\operatorname {E} {\big [}(f-\operatorname {E} [{\hat {f}}])^{2}{\big ]}+\operatorname {E} [\varepsilon ^{2}]+\operatorname {E} {\big [}(\operatorname {E} [{\hat {f}}]-{\hat {f}})^{2}{\big ]}+2\operatorname {E} {\big [}(f-\operatorname {E} [{\hat {f}}])\varepsilon {\big ]}+2\operatorname {E} {\big [}\varepsilon (\operatorname {E} [{\hat {f}}]-{\hat {f}}){\big ]}+2\operatorname {E} {\big [}(\operatorname {E} [{\hat {f}}]-{\hat {f}})(f-\operatorname {E} [{\hat {f}}]){\big ]}\\&=(f-\operatorname {E} [{\hat {f}}])^{2}+\operatorname {E} [\varepsilon ^{2}]+\operatorname {E} {\big [}(\operatorname {E} [{\hat {f}}]-{\hat {f}})^{2}{\big ]}+2(f-\operatorname {E} [{\hat {f}}])\operatorname {E} [\varepsilon ]+2\operatorname {E} [\varepsilon ]\operatorname {E} {\big [}\operatorname {E} [{\hat {f}}]-{\hat {f}}{\big ]}+2\operatorname {E} {\big [}\operatorname {E} [{\hat {f}}]-{\hat {f}}{\big ]}(f-\operatorname {E} [{\hat {f}}])\\&=(f-\operatorname {E} [{\hat {f}}])^{2}+\operatorname {E} [\varepsilon ^{2}]+\operatorname {E} {\big [}(\operatorname {E} [{\hat {f}}]-{\hat {f}})^{2}{\big ]}\\&=(f-\operatorname {E} [{\hat {f}}])^{2}+\operatorname {Var} [y]+\operatorname {Var} {\big [}{\hat {f}}{\big ]}\\&=\operatorname {Bias} [{\hat {f}}]^{2}+\operatorname {Var} [y]+\operatorname {Var} {\big [}{\hat {f}}{\big ]}\\&=\operatorname {Bias} [{\hat {f}}]^{2}+\sigma ^{2}+\operatorname {Var} {\big [}{\hat {f}}{\big ]}\\\end{aligned}

Bias v.s. Variance

Alt text

  • 简单模型,具有较大的bias,但是variance较小
  • 复杂模型,具有较小的bias,但是variance较大

bias太大怎么办?

  • 判断方法
    • 如果模型无法拟合训练样本,那么bias就会很大——欠拟合
    • 如果模型能够很好地拟合训练样本,但是测试误差很大,那么模型就有很大的variance——过拟合
  • 针对bias问题,需要重新设计模型:
    • 添加新的特征
    • 使用更加复杂的模型

variance太大怎么办?

  • 收集更多训练样本
  • 正则化=>可能导致bias增大

模型选择[2]

1.总是使用交叉验证,而非使用单一的验证集。
2.相信交叉验证分数,而非排行榜分数。
3.最终模型选择2个得分最高且差异最大的模型。

  • 交叉验证
    Alt text
  • N折交叉验证
    Alt text

引用

[1].https://en.wikipedia.org/wiki/Bias%E2%80%93variance_tradeoff
[2].http://www.chioka.in/how-to-select-your-final-models-in-a-kaggle-competitio/

台大机器学习2017课程笔记-回归

发表于 2019-04-03 | 分类于 machine learning

回归:输出为数值

  • 股票市场预测=>道琼斯指数
  • 自动驾驶汽车=>方向盘角度
  • 推荐系统=>购买可能性

线性回归

模型
损失函数
最优化
  • 梯度下降
    针对参数$w$,考虑损失函数 $L(w) $
    • 1.随机选择初始值$w^0$
    • 2.计算$\frac{\mathrm{d} L}{\mathrm{d} w}|_{w=w^0}$
    • 3.更新$w_1\leftarrow w_0-\eta \frac{\mathrm{d} L}{\mathrm{d} w}|_{w=w^0}$
      Alt text
  • 存在问题:
    • 驻点
    • 局部最小值
      模型选择
  • 过拟合问题
    Alt text
    Alt text

    • 这5个模型都是线性模型(是否线性模型看w的次数)
    • 更复杂的模型具有更小的训练误差
    • 更复杂的模型不一定具有更小的泛化误差(发生过拟合)
  • 正则化

    • $ L(w,b) = \sum_{n}(\hat{y}^n-(b+\sum w_ix_i))^2 + \lambda \sum(w_i)^2$
    • 更小的$w_i$值代表模型更平滑(考虑下面方程,当$x$变化$\delta x$时,$y$变化为$\sum w_i \delta x_i$,当$w$越小时,$y$变化越小)

台大机器学习2019课程笔记-对抗攻击

发表于 2019-03-26 | 更新于 2019-04-03 | 分类于 machine learning

动机

  • 学习算法需要在现实世界中应用
  • 对于噪声应具有鲁棒性
  • 能够应付恶意攻击
  • 应用诸如垃圾邮件分类、恶意软件检测、网络入侵检测等

攻击

1.如何做?

  • $x$->Network->class a
  • $x+\delta x$->Network->class b

2.攻击损失函数
Alt text

  • 训练:$L_{train}(\theta)=C(y^{0},y^{true})$ => $x$固定不变,调整$\theta$
  • 无目标攻击:$L({x}’)=-C({y}’,y^{true})$ => $\theta$固定不变,调整$x$
  • 有目标攻击:$L({x}’)=-C({y}’,y^{true})+C({y}’,y^{false})$
  • 距离约束:$d(x^{0},{x}’)\leqslant \varepsilon $ => 保证攻击不被发现

3.约束

  • L2-norm
  • L-infinity
  • 具体采用何种距离约束取决于不同的任务。就视觉系统来说,L-infinity更好。
阅读全文 »

台大机器学习2019课程笔记-异常检测

发表于 2019-03-22 | 分类于 machine learning

问题定义

  • 给定一组训练数据{$x^{1}$,$x^{2}$,…,$x^{N}$}
  • 找到判断输入数据是否与训练数据相似的一个映射关系
  • x与训练集相似,输出正常;否则输出异常
  • 不同的目的使用不同的方法来定义相似性
    Alt text

什么是异常?

Alt text

应用

  • 欺诈检测[1][2]
  • 网络入侵侦测[3]
  • 癌症检测[4]

二值分类

方法:
  • 给定正常数据{$x^{1}$,$x^{2}$,…,$x^{N}$}=>class 1
  • 给定异常数据{$\widetilde{x}^1$,$\widetilde{x}^2$,…,$\widetilde{x}^N$}=>class 2
  • 训练一个二值分类器
困难:
  • 难以定义异常类,例如:$x$表示宝可梦,但是$\widetilde{x}$可以为任何东西
  • 异常的情况难以收集

分类

  • 有标记数据=>使用分类器分类
  • 没有标记数据
    Alt text
阅读全文 »

提升效率的10个pandas技巧

发表于 2019-03-15 | 更新于 2019-03-25 | 分类于 python

原文链接:https://towardsdatascience.com/10-python-pandas-tricks-that-make-your-work-more-efficient-2e8e483808ba

Pandas是一个广泛用于处理结构化数据的Python包。网络上已经有很多很好的教程,但在这里我还是想介绍一些读者可能以前不知道的很酷的技巧,我相信它们很有用。

read_csv

每个人都知道这个命令。但是当你想要读取的数据很大时,那么在加载整个表之前,可以尝试添加这个参数:nrows = 5来读取表的一小部分。这样你就可以避免选择了错误分隔符这类问题(分隔符并非总是逗号形式)。

(或者,您可以在linux中使用’head’命令检查任何文本文件中的前5行(例如):head -c 5 data.txt)

然后,您可以通过使用df.columns.tolist()提取所有列来提取列列表,然后添加usecols = ['c1','c2',...]参数来加载所需的列。此外,如果您知道几个特定列的数据类型,还可以通过添加参数dtype = {'c1':str,'c2':int,...},来加快加载速度。此参数的另一个优点是,如果您有一个包含字符串和数字的列,那么将其类型声明为字符串是一个好习惯,这样在尝试使用此列作为键合并表时就不会出错。

select_dtypes

如果数据预处理必须在Python中完成,那么这个命令可以节省你一些时间。读入表后,每列的默认数据类型可以是bool,int64,float64,object,category,timedelta64或者datetime64。您可以通过下面的命令查看数据分布情况来了解dataframe中的数据类型

1
df.dtypes.value_counts()

然后执行

1
df.select_dtypes(include=[‘float64’, ‘int64’])

来选择数值类型的dataframe。

copy

如果您还没有听说过,那么这是一个重要的命令。执行以下语句:

1
2
3
4
5
import pandas as pd
df1 = pd.DataFrame({ 'a':[0,0,0], 'b': [1,1,1]})
df2 = df1
df2['a'] = df2['a'] + 1
df1.head()

你会发现df1已经改变了。这是因为df2 = df1没有复制df1并将其分配给df2,而是设置了指向df1的指针。因此,df2的任何变化都会导致df1发生变化。要解决这个问题,你可以采取下面的方法

1
df2 = df1.copy()

或者

1
2
from copy import deepcopy
df2 = deepcopy(df1)

阅读全文 »
12…12
Lu Xiaohua

Lu Xiaohua

116 日志
33 分类
86 标签
GitHub E-Mail
© 2019 Lu Xiaohua
由 Hexo 强力驱动 v3.8.0
|
主题 – NexT.Muse v7.0.0