Skip to main content

Command Palette

Search for a command to run...

《深度学习入门》的cpp实现-ch01: Eigen的基本操作

Published
《深度学习入门》的cpp实现-ch01: Eigen的基本操作

在《深度学习入门》这本书中使用 numpy 进行数据的操作和运算,我们要使用cpp来实现,因此选用了Eigen这个库, 下面是 Eigen 的一些基本操作。


void create_matrix() {
    // 在Eigen中, 向量和矩阵都是 Matrix 模版类的对象

    // 创建矩阵需要Matrix<>构造, 前三个为必须参数,后三个为可选参数
    // 第一个参数为数据类型,后两个参数分别为矩阵的行数和列数
    Eigen::Matrix<double, 3, 4> mat_34d;
    std::cout << mat_34d << std::endl;

    // 当在编译器确定不了矩阵大小,可以使用 Eigen::Dynamic 来生成动态矩阵
    Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic> mat_dyna_i;
    // 使用 resize() 确定矩阵大小, 这里的 resize() 对矩阵是"毁灭性"的, 如果只改变大小,需要使用 conservativeResize()
    mat_dyna_i.resize(2, 2);
    std::cout << mat_dyna_i << std::endl;

    // Eigen 对于小于等于4的矩阵创建,已经定义好类型了, 格式为 Matrix{row}{col}{type}
    // 下面就是定一个 2*2的 double 类型矩阵, 2代表了行数为2, 当行数和列数相同时省略, d代表 type,即double类型
    Eigen::Matrix2d mat_2d;
    mat_2d(1, 0) = 10;
    std::cout << mat_2d << std::endl;

    // 3 * 3 类型为 int 的矩阵
    Eigen::Matrix3i mat_3i;

    // 当 row 或者 col不确定的时候,使用X表示.
    // 2 * x 类型为 int 矩阵
    Eigen::Matrix2Xi mat_2xi;
}

void operate_matrix() {
    // 使用 逗号表达式 可为矩阵复制
    Eigen::Matrix2d mat_2d;
    mat_2d << 1, 2, 3, 4;
    std::cout << mat_2d << std::endl;

    // 可以是用 m(1, 2) 的方式获取和修改矩阵中的元素
    mat_2d(0, 0) = -1;
    std::cout << mat_2d << std::endl;

    // rows(), cols() 获取行数和列数
    std::cout << "rows: " << mat_2d.rows() << " cols: " << mat_2d.cols() << std::endl;

    // Eigen 重载了 + - * / 操作,但是 Eigen本身对数据敏感,不支持不同数据类型见的自动转化
    // + - 操作不支持矩阵和标量直接操作,如果需要整体 + 或者 - 某个数值,需要构建一个相同的矩阵。
    Eigen::Matrix2i mat_2i;
    mat_2i << 1, 2, 3, 4;
    int delta = 10;
    Eigen::Matrix2i mat_plus10 = mat_2i +
            Eigen::MatrixXi::Constant(mat_2i.rows(), mat_2i.cols(), delta);
    std::cout << mat_plus10 << std::endl;

    std::cout << mat_2i * 10 << std::endl;

    // 所有向量默认为列向量, 如果想用行向量需要使用 RowVector
    Eigen::Vector3d vec_3d(1, 2, 3);
    // 标量积即每个分量乘以相同数,结果还是一个向量,对应向量的缩放
    std::cout << "* result: " << vec_3d * 3 << std::endl;
    // 点积是两个向量每个分量对应相乘再相加,结果为标量,两个向量之间的夹角,以及在b向量在a向量方向上的投影
    std::cout << "dot result: " << vec_3d.dot(vec_3d) << std::endl;
    // 叉积是将两个向量按叉积公式展开相乘,结果仍然是一个向量,并且两个向量的叉积与这两个向量组成的坐标平面垂直
    std::cout << "cross result: " << vec_3d.cross(vec_3d) << std::endl;

    // colwise() 返回矩阵每列的值
    // rowwise() 返回矩阵每行的值
    // 可以用来实现 矩阵 和 向量的广播操作
    Eigen::Matrix<int, 2, 4> mat_24_i;
    mat_24_i << 1, 2,  3, 4,  5, 6,  7, 8;
    std::cout << mat_24_i << std::endl;
    Eigen::Vector2i vec2;
    vec2 << 10, 20;
    // 第一行 +10, 第二行 +20
    Eigen::Matrix<int, 2, 4> res1 = mat_24_i.colwise() + vec2;
    std::cout << res1 << std::endl;
}

void block() {
    //  在Eigen中有两种方式进行块操作,
    //  一种是动态尺寸块(dynamic-size block):mat.block(i,j,p,q),
    //  另一种是固定尺寸块(fixed-size block):mat.block<p,q>(i,j)。
    //  它们在语义上等价,都表示从矩阵的i行j列开始取大小为p行q列的块
    Eigen::MatrixXd m = Eigen::MatrixXd::Random(5, 5);
    std::cout << m.block<3, 1>(0, 0) << std::endl;
    std::cout << m.block(0, 0, 3, 1) << std::endl;

    // row() col() 可取单独一行/列
    std::cout << m.row(0) << std::endl;
}

void eigen_map() {
    // Map 主要用于使用C++原生数组生成 Matrix
    // 需要3个模板参数分别是数据类型、行数、列数。而如果采用预定义类型,则直接传入即可,不需要模板参数了。
    // 此外Map的构造函数中还需要两个其它参数:指向数据的指针以及修改后矩阵的大小
    double data[8];
    for (int i = 0; i < 8; ++i) {
        data[i] = i;
    }
    Eigen::Map<Eigen::MatrixXd> md1(data, 2, 4);
    std::cout << md1 << std::endl;
    // 需要注意的是Map构造默认是按照列优先的,即按照列依次从上倒下、从左到右填充,
    // 若想换成行优先只需要在模板参数里加上RowMajor即可。
    Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> md2(data, 2, 4);
    std::cout << md2 << std::endl;
}

void test_eigen() {
    eigen_map();

}

More from this blog

<Programming with Types>随想: Chapter 2. Basic types

类型限制了一个变量可以接受的有效值的集合,对数据可以进行的操作,数据的意义。 空类型(The empty type) 根据类型的定义,类型定义了可以接受的有效值集合,那么这个集合有没有可能为空?答案是有可能的,TypeScirpt 的 never 就是这种类型。 需要注意的是,空类型不同与 void, 后者是有效值集合当中只有一个值,但这个值没有任何意义。而空类型的有效值集合本身是空的。 使用场景 控制流分析 在函数调用时,标志一个函数不会返回任何值: 在调用过程中抛出异常、死循环或者程序崩溃...

Mar 5, 2023
<Programming with Types>随想: Chapter 2. Basic types

精读《设计机器学习系统》-ch04: 训练数据

不同于 Chapter03 从系统的角度来处理数据,这一章从数据科学的视角来处理数据。这章的标题是“training Data”,而非“training dataset”,因为 数据集(dataset) 意味着有限(finite)和固定(stationary), 而现实生产环境中的 数据(data) 通常是 无限 并且 不固定 的。 抽样 抽样方法在 ML 项目的生命周期中无处不在,在这一节中,我们使用生成训练数据作为例子。 那为什么需要抽样?直接使用全部数据不可以吗? 首先,在现实世界中,并不...

Dec 10, 2022
精读《设计机器学习系统》-ch04: 训练数据
7

70 Talk

19 posts