一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情。
概述
首先这是一个入门级的分享,所有其中给出了一些详细解释,如果您对神经网络有了一定了解可以跳过。
动机
看过安德烈、卡帕奇分享,其中他就推荐大家在学习神经网络时,最好自己动手去实现一个简单网络,而不是上来就用 pytorch 或者 tf 来实现一些复杂网络。因此自己冒出自己写一个网络想法。
开发环境
系统 Ubuntu 基于 cpu ,语言这里选择了 c++ ,处于个人现在正在学习 c++ 所以选择了 c++ 开发网络,随便练一练手。
cmake_minimum_required(VERSION 3.1)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(hello VERSION 1.0)
file(GLOB_RECURSE SRC_FILES src/*.cpp)
add_executable(hello main.cpp ${SRC_FILES})
target_include_directories(hello PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
有关 cmake 可以简单参照一下 简单介绍一下 cmake 使用方法(1)
这里还是简单说一下,项目源文件放在 src 目录下,头文件放置了 include 文件夹下,然后我们在 include 文件夹下创建一个 tensor.h 头文件。
#pragma once
#include <vector>
#include <cmath>
#include <iostream>
#ifndef TENSOR_H_FILE
#define TENSOR_H_FILE
class Tensor
{
public:
__uint32_t _cols;
__uint32_t _rows;
std::vector<float> _vals;
public:
Tensor(__uint32_t cols,__uint32_t rows):_cols(cols),_rows(rows),_vals({})
{
_vals.resize(cols*rows,0.0f);
}
};
#endif //TENSOR_H_FILE
这里定义一个类,暂时 tensor 可以看做一个 2D 的矩阵,只有行和列,并且在存在这个 tensor 时我们用到 vector 这个动态数组,将其展平存在为一个 1D 矩阵,也可以理解为一个向量或者更直就可以将其看做为一个数组,长度为 cols x rows
at 函数实现
函数 at 可以通过 col 和 row 可以定位到矩阵某一个元素。
float at(__uint32_t col, __uint32_t row)
{
return _vals[row*_cols + col];
}
float &at(__uint32_t col, __uint32_t row)
{
return _vals[row*_cols + col];
}
Tensor multiply(Tensor& target)
{
assert(_cols == target._rows);
Tensor output(target._cols,_rows);
for(__uint32_t y = 0; y < output._rows; y++){
for(__uint32_t x = 0; x < output._cols; x++)
{
float result = 0.0f;
for(__uint32_t k = 0; k < _cols; k++)
result += at(k,y) * target.at(x,k);
output.at(x,y) = result;
}
}
return output;
};
有关矩阵相乘,我们可以翻一翻线性代数的矩阵
float &at(__uint32_t col, __uint32_t row)
{
return _vals[row*_cols + col];
}
Tensor multiply(Tensor& target)
{
assert(_cols == target._rows);
Tensor output(target._cols,_rows);
// iterate output
for(__uint32_t y = 0; y < output._rows; y++){
for(__uint32_t x = 0; x < output._cols; x++)
{
float result = 0.0f;
for(__uint32_t k = 0; k < _cols; k++)
result += at(k,y) * target.at(x,k);
output.at(x,y) = result;
}
}
return output;
};
add 函数
矩阵加法
Tensor add(Tensor& target)
{
assert(_rows == target._rows && _cols == target._cols);
Tensor output(_cols,_rows);
for(__uint32_t y = 0; y < output._rows; y++){
for(__uint32_t x = 0; x < output._cols; x++)
{
output.at(x,y) = at(x,y) + target.at(x,y);
}
}
return output;
}
multiplyScaler
Tensor multiplyScaler(float s)
{
Tensor output(_cols,_rows);
// iterate output
for(__uint32_t y = 0; y < output._rows; y++){
for(__uint32_t x = 0; x < output._cols; x++)
{
output.at(x,y) = at(x,y) * s;
}
}
return output;
}
addScaler
Tensor addScaler(float s)
{
Tensor output(_cols,_rows);
// iterate output
for(__uint32_t y = 0; y < output._rows; y++){
for(__uint32_t x = 0; x < output._cols; x++)
{
output.at(x,y) = at(x,y) + s;
}
}
return output;
}
negative
对于矩阵的元素求负,这样做好处就是我们可以将减法通过 negative 来实现
Tensor negative()
{
Tensor output(_cols,_rows);
for(__uint32_t y = 0; y < output._rows; y++){
for(__uint32_t x = 0; x < output._cols; x++)
{
output.at(x,y) = -at(x,y);
}
}
return output;
}
transpose
矩阵转置,也就是行列互换。
Tensor transpose()
{
Tensor output(_cols,_rows);
for(__uint32_t y = 0; y < _rows; y++){
for(__uint32_t x = 0; x < _cols; x++)
{
output.at(y,x) = at(x,y);
}
}
return output;
}