范数介绍及C++/OpenCV/Eigen的三种实现

有时我们需要衡量一个向量的大小。在机器学习中,我们经常使用被称为范数(norm)的函数衡量向量大小。形式上,Lp范数定义如下:


范数(包括Lp范数)是将向量映射到非负值的函数。直观上来说,向量x的范数衡量从原点到点x的距离。更严格地说,范数是满足下列性质的任意函数:


当p=2时,L2范数被称为欧几里得范数(Euclidean norm)。它表示从原点出发到向量x确定的点的欧几里得距离。

在数学中,欧几里得距离或欧几里得度量是欧几里得空间中两点间”普通”(即直线)距离。使用这个距离,欧式空间成为度量空间。相关联的范数称为欧几里得范数。


L2范数在机器学习中出现地十分频繁,经常简化表示为‖x‖,略去了下标2。平方L2范数也经常用来衡量向量的大小,可以简单地通过点积xTx计算。

平方L2范数在数学和计算上都比L2范数本身更方便。例如,平方L2范数对x中每个元素的导数只取决于对应的元素,而L2范数对每个元素的导数却和整个向量相关。但是在很多情况下,平方L2范数也可能不受欢迎,因为它在原点附件增长得十分缓慢。在某些机器学习应用中,区分恰好是零的元素和非零但值很小的元素是很重要的。在这些情况下,我们转而使用在各个位置斜率相同,同时保持简单的数学形式的函数:L1范数。

当机器学习问题中零和非零元素之间的差异非常重要时,通常会使用L1范数。每当x中某个元素从0增加ε,对应的L1范数也会增加ε。

有时候我们会统计向量中非零元素的个数来衡量向量的大小。有些作者将这种函数称为"L0范数",但是这个术语在数学意义上是不对的。向量的非零元素的数目不是范数,因为对向量缩放α倍不会改变向量非零元素的数目。因此,L1范数经常作为表示非零元素数目的替代函数。

另外一个经常在机器学习中出现的范数是L范数,也被称为最大范数(max norm)。这个范数表示向量中具有最大幅值的元素的绝对值:‖x‖=max|xi|

有时候我们可能也希望衡量矩阵的大小。在深度学习中,最常见的做法是使用Frobenius范数:类似于向量的L2范数。


两个向量的点积(dot product)可以用范数来表示。具体地,xTy=‖x‖2‖y‖2cosθ,其中θ表示x和y之间的夹角。

以上内容摘自: 《深度学习中文版》 和 维基百科 

以下是分别用C++、OpenCV、Eigen实现的求范数正无穷、范数L1、范数L2的code:

C++和OpenCV code:

#include "funset.hpp"
#include <math.h>
#include <iostream>
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>
#include "common.hpp"

// 求范数
typedef enum Norm_Types_ {
	Norm_INT = 0, // 无穷大
	Norm_L1, // L1
	Norm_L2 // L2
} Norm_Types;

template<typename _Tp>
int norm(const std::vector<std::vector<_Tp>>& mat, int type, double* value)
{
	*value = 0.f;

	switch (type) {
		case Norm_INT: {
			for (int i = 0; i < mat.size(); ++i) {
				for (const auto& t : mat[i]) {
					*value = std::max(*value, (double)(fabs(t)));
				}
			}
		}
			break;
		case Norm_L1: {
			for (int i = 0; i < mat.size(); ++i) {
				for (const auto& t : mat[i]) {
					*value += (double)(fabs(t));
				}
			}
		}
			break;
		case Norm_L2: {
			for (int i = 0; i < mat.size(); ++i) {
				for (const auto& t : mat[i]) {
					*value += t * t;
				}
			}
			*value = std::sqrt(*value);
		}
			break;
		default: {
			fprintf(stderr, "norm type is not supported\n");
			return -1;
		}
	}

	return 0;
}

int test_norm()
{
	fprintf(stderr, "test norm with C++:\n");
	std::vector<int> norm_types{ 0, 1, 2 }; // 正无穷、L1、L2
	std::vector<std::string> str{ "Inf", "L1", "L2" };

	// 1. vector
	std::vector<float> vec1{ -2, 3, 1 };
	std::vector<std::vector<float>> tmp1(1);
	tmp1[0].resize(vec1.size());
	for (int i = 0; i < vec1.size(); ++i) {
		tmp1[0][i] = vec1[i];
	}

	for (int i = 0; i < str.size(); ++i) {
		double value{ 0.f };
		norm(tmp1, norm_types[i], &value);

		fprintf(stderr, "vector: %s: %f\n", str[i].c_str(), value);
	}

	// 2. matrix
	std::vector<float> vec2{ -3, 2, 0, 5, 6, 2, 7, 4, 8 };
	const int row_col{ 3 };
	std::vector<std::vector<float>> tmp2(row_col);
	for (int y = 0; y < row_col; ++y) {
		tmp2[y].resize(row_col);
		for (int x = 0; x < row_col; ++x) {
			tmp2[y][x] = vec2[y * row_col + x];
		}
	}

	for (int i = 0; i < str.size(); ++i) {
		double value{ 0.f };
		norm(tmp2, norm_types[i], &value);

		fprintf(stderr, "matrix: %s: %f\n", str[i].c_str(), value);
	}

	fprintf(stderr, "\ntest norm with opencv:\n");
	norm_types[0] = 1; norm_types[1] = 2; norm_types[2] = 4; // 正无穷、L1、L2
	cv::Mat mat1(1, vec1.size(), CV_32FC1, vec1.data());

	for (int i = 0; i < norm_types.size(); ++i) {
		double value = cv::norm(mat1, norm_types[i]);
		fprintf(stderr, "vector: %s: %f\n", str[i].c_str(), value);
	}

	cv::Mat mat2(row_col, row_col, CV_32FC1, vec2.data());
	for (int i = 0; i < norm_types.size(); ++i) {
		double value = cv::norm(mat2, norm_types[i]);
		fprintf(stderr, "matrix: %s: %f\n", str[i].c_str(), value);
	}

	return 0;
}
执行结果如下:

Eigen code:

#include "funset.hpp"
#include <math.h>
#include <iostream>
#include <vector>
#include <string>
#include <opencv2/opencv.hpp>
#include <Eigen/Dense>
#include "common.hpp"

int test_norm()
{
	fprintf(stderr, "test norm with eigen:\n");
	// 1. vector
	std::vector<float> vec1{ -2, 3, 1 };
	Eigen::VectorXf v(vec1.size());
	for (int i = 0; i < vec1.size(); ++i) {
		v[i] = vec1[i];
	}

	double value = v.lpNorm<Eigen::Infinity>();
	fprintf(stderr, "vector: Inf: %f\n", value);
	value = v.lpNorm<1>();
	fprintf(stderr, "vector: L1: %f\n", value);
	value = v.norm(); // <==> sqrt(v.squaredNorm()) <==> v.lpNorm<2>()
	fprintf(stderr, "vector: L2: %f\n", value);

	// 2. matrix
	std::vector<float> vec2{ -3, 2, 0, 5, 6, 2, 7, 4, 8 };
	const int row_col{ 3 };
	Eigen::Map<Eigen::MatrixXf> m(vec2.data(), row_col, row_col);

	value = m.lpNorm<Eigen::Infinity>();
	fprintf(stderr, "matrix: Inf: %f\n", value);
	value = m.lpNorm<1>();
	fprintf(stderr, "matrix: L1: %f\n", value);
	value = m.norm();
	fprintf(stderr, "matrix: L2: %f\n", value);

	return 0;
}
执行结果如下:

可见使用C++、OpenCV、Eigen三种方法实现的结果是一致的。


GitHub:

https://github.com/fengbingchun/NN_Test

https://github.com/fengbingchun/Eigen_Test

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值