kaldi中的Vector和Matrix
Vector和Matrix是kaldi中最常用的数据类型之一。语音数据,提取的特征,计算的结果,都保存在Vector或者Matrix之中。按照字面意思,Vector是“向量”,它只有一行数据,是一维的。Matrix是“矩阵”,它有行与列两个维度。kaldi中的Vector和Matrix,可以做许多数学上的操作。比如点加或点乘(每个元素都加上一个数,或者乘以一个数),比如矩阵之间的乘法和矩阵的奇异分解等。kaldi中Vector和Matrix还可以做一些特殊操作,比如对每个元素取对数,对所有元素做softmax等。
一、首先介绍下Vector。在matrix/kaldi-vector.h中,定义了三个类:VectorBase、Vector和SubVector。其中,VectorBase是基类(父类),Vector和SubVector是派生类(子类)。VectorBase中的成员函数已经可以完成一个向量类的所有操作了,Vector类只是做了封装,定义了多种形式的构造函数,增加了resize操作等。
VectorBase类中的数据成员十分简单,就两个成员。一个指针data_指向存放数据的内存,一个整数dim_指示元素的个数。
VectorBase中的函数成员比较多,但基本可以分为两类。一类是基本的、简单的操作。例如SetZero函数,用以设置全部数据为0;例如max函数,返回向量中的最大值。另一类是偏应用的函数或操作。比如,ApplySoftMax函数,提供softmax操作;比如Norm函数,计算范数。
下面的代码,有利于理解VectorBase的各种性质。注意,为了方面阅读和理解,对源代码做了修改。
template<typename Real>
class VectorBase {
// *******数据成员,data_表示内存地址,dim_表示元素个数*******
Real* data_;
MatrixIndexT dim_;
explicit VectorBase(): data_(NULL), dim_(0) { }
// **************第一类,比较基础和简单的函数*******************
// set类函数,设置全部值为0、特定值或者某种分布的随机值
void SetZero();
void Set(Real f);
void SetRandn();
void SetRandUniform();
// 返回元素个数dim_,返回元素地址data_,返回占用内存大小,重载()操作等
inline MatrixIndexT Dim() const { return dim_; }
inline Real* Data() { return data_; }
inline MatrixIndexT SizeInBytes() const { return (dim_*sizeof(Real)); }
inline Real operator() (MatrixIndexT i) const { return *(data_ + i); }
inline Real & operator() (MatrixIndexT i) { return *(data_ + i); }
Real Max() const;
Real Min() const;
Real Sum() const;
Real SumLog() const;
// 拷贝向量或者矩阵(全部或者局部,例如一行)的内容,来作为data_
void CopyFromVec(const VectorBase<Real> &v);
void CopyFromPacked(const PackedMatrix<Real> &M);
void CopyRowsFromMat(const MatrixBase<Real> &M);
void CopyColsFromMat(const MatrixBase<Real> &M);
void CopyRowFromMat(const MatrixBase<Real> &M, MatrixIndexT row);
void CopyDiagFromMat(const MatrixBase<Real> &M);
// **************第二类,偏应用的操作和函数*******************
void Add(Real c); /// data_[i] += c;
void Scale(Real c); /// data_[i] *= c; cblas_Xscal(dim_, c, data_, 1);
void ApplyLog(); /// data_[i] = Log(data_[i])
void ApplyExp(); /// data_[i] = Exp(data_[i])
void ApplyAbs(); /// data_[i] = abs(data_[i])
void InvertElements(); /// data_[i] = 1 / data_[i]
void ApplyPow(Real power); // 求指数
Real Norm(Real p) const; // 求p阶范数
void MulElements(const VectorBase<Real> &v); //data_[i] *= v.data_[i];
void DivElements(const VectorBase<Real> &v); //data_[i] /= v.data_[i];
//各种形式的矩阵操作,一般调用BLAS,例如 AddVec: *this = *this + alpha * rv
void AddVec(const Real alpha, const VectorBase<Real> &v);
void AddVec2(const Real alpha, const VectorBase<Real> &v); // rv^2
void AddMatVec(...); // this <-- beta*this + alpha*M*v.
void AddSpVec(...) // this <-- beta*this + alpha*M*v.
void AddTpVec(...) // this <-- beta*this + alpha*M*v.
void AddVecVec(...); // this <-- alpha * v .* r + beta*this .
void AddVecDivVec(...);// this <---- alpha*v/r + beta*this
void MulTp(...); // *this <-- *this *M
//使用softmax: \f$ x(i) = exp(x(i)) / \sum_i exp(x(i)) \f$
Real ApplySoftMax(){
Real max = this->Max(), sum = 0.0;
for (MatrixIndexT i = 0; i < dim_; i++)
sum += (data_[i] = Exp(data_[i] - max));
this->Scale(1.0 / sum);
return max + Log(sum);
}
void Tanh(const VectorBase<Real> &src);
void Sigmoid(const VectorBase<Real> &src);
}; // class VectorBase
template<typename Real>
class Vector: public VectorBase<Real> {
public:
// 各种构造函数和赋值操作。
Vector(): VectorBase<Real>() {}
explicit Vector(const MatrixIndexT s, MatrixResizeType resize_type)
: VectorBase<Real>() { Resize(s, resize_type); }
Vector(const Vector<Real> &v) : VectorBase<Real>() {
Resize(v.Dim(), kUndefined);
this->CopyFromVec(v); }
explicit Vector(const VectorBase<Real> &v) : VectorBase<Real>() {
Resize(v.Dim(), kUndefined);
this->CopyFromVec(v); }
Vector<Real> &operator = (const Vector<Real> &other) {
Resize(other.Dim(), kUndefined);
this->CopyFromVec(other);
return *this; }
// 新增的Swap、Resize和RemoveElement操作
void Swap(Vector<Real> *other);
void Resize(MatrixIndexT length, MatrixResizeType resize_type = kSetZero);
void RemoveElement(MatrixIndexT i);
private:
void Init(const MatrixIndexT dim);
void Destroy();
};
template<typename Real>
class SubVector : public VectorBase<Real> {
public:
//SubVector不分配内存,它使用其他VectorBase的数据,可以看作是“引用”。
// 下面是各种版本的构造函数。
SubVector(const VectorBase<Real> &t, const MatrixIndexT origin,
const MatrixIndexT length) : VectorBase<Real>() {
VectorBase<Real>::data_ = const_cast<Real*> (t.Data()+origin);
VectorBase<Real>::dim_ = length;
}
SubVector(const PackedMatrix<Real> &M) {
VectorBase<Real>::data_ = const_cast<Real*> (M.Data());
VectorBase<Real>::dim_ = (M.NumRows()*(M.NumRows()+1))/2;
}
SubVector(const SubVector &other) : VectorBase<Real> () {// Copy constructor
VectorBase<Real>::data_ = other.data_;
VectorBase<Real>::dim_ = other.dim_;
}
SubVector(Real *data, MatrixIndexT length) : VectorBase<Real> () {
VectorBase<Real>::data_ = data;
VectorBase<Real>::dim_ = length;
}
SubVector(const MatrixBase<Real> &matrix, MatrixIndexT row) {
VectorBase<Real>::data_ = const_cast<Real*>(matrix.RowData(row));
VectorBase<Real>::dim_ = matrix.NumCols();
}
~SubVector() {} ///< Destructor (does nothing; no pointers are owned here).
private:
/// Disallow assignment operator.
SubVector & operator = (const SubVector &other) {}
};通过上面的代码,我们可以看出,Vector对VectorBase并未做太多的扩展,它们的功能基本一样。SubVector可以看作一种“引用”,它自身并不分配内存保存数据,而是指向了其他的对象中的数据。
二、简单介绍下Matrix。
跟Vector类似,在在matrix/kaldi-matrix.h中,定义了三个类:MatrixBase、Matrix和SubMatrix。MatrixBase是基类,另外两个是派生类。MatrixBase已经实现了非常多的方法。Matrix只是在基类的基础上,加了少数几个函数,比如Swap和RemoveRow等,这点跟Vector与VectorBase的关系一样。
MatrixBase中,数据成员并不多,大部分也容易理解。比如,整数num_rows_和num_cols_表示矩阵的行数和列数,指针data_指向保存数据的内存地址。这里有另外一个整型变量stride_需要注意。stride_保存的是正真的一行的个数。这里的意思是,一个矩阵,一行可能可以存放许多数据(stride_个),但可以不放满,只放num_cols_个。这时,一部分空间是浪费的。当然,一般部分情况下,num_cols_和stride_是一致的。
在矩阵上面的操作要比向量上的操作多,所以Matrix中的成员函数比Vector中的多很多。
template<typename Real>
class MatrixBase {
//***************数据成员********************
Real* data_; // data memory area
MatrixIndexT num_cols_; // < Number of columns
MatrixIndexT num_rows_; // < Number of rows
MatrixIndexT stride_; // True number of columns
// 基本操作函数
inline MatrixIndexT NumRows() const { return num_rows_; }
inline MatrixIndexT NumCols() const { return num_cols_; }
inline MatrixIndexT Stride() const { return stride_; }
inline Real* Data() const { return data_; }
inline Real* RowData(MatrixIndexT i) { return data_ + i * stride_; }
inline Real& operator() ( r, c) {return *(data_ + r * stride_ + c); }
size_t SizeInBytes() const {return num_rows_ * stride_ * sizeof(Real);}
Real &Index (MatrixIndexT r, MatrixIndexT c) { return (*this)(r, c); }
// set、max、min等函数,省略若干
void SetZero();
void Set(Real);
Real Sum() const;
Real Max() const;
Real Min() const;
bool IsZero(Real cutoff = 1.0e-05) const;
//Copy、SubVector、SubMatrix类函数,很多版本
void CopyFromMat(const CompressedMatrix &M);
void CopyRowsFromVec(const VectorBase<Real> &v);
void CopyDiagFromVec(const VectorBase<Real> &v);
inline SubVector<Real> Row(MatrixIndexT i);
inline SubMatrix<Real> Range(...);
// 一些加减乘除操作,其他应用操作
void MulElements(const MatrixBase<Real> &A);
void DivElements(const MatrixBase<Real> &A);
void Scale(Real alpha);
void Max(const MatrixBase<Real> &A);
void Min(const MatrixBase<Real> &A);
void MulColsVec(const VectorBase<Real> &scale);
void MulRowsVec(const VectorBase<Real> &scale);
void Add(const Real alpha);
void ApplyFloor(Real floor_val);
void ApplyCeiling(Real ceiling_val);
void ApplyLog();
void ApplyExp();
Real ApplySoftMax();
void Sigmoid(const MatrixBase<Real> &src);
// 求正定矩阵、求逆;转置;特征分解;奇异值分解;矩阵运算
Real LogDet(Real *det_sign = NULL) const;
void Invert(Real *log_det = NULL, Real *det_sign = NULL,
bool inverse_needed = true);
void Transpose();
void Eig(MatrixBase<Real> *P,
VectorBase<Real> *eigs_real,
VectorBase<Real> *eigs_imag) const;
void Svd(VectorBase<Real> *s, MatrixBase<Real> *U,
MatrixBase<Real> *Vt) const;
void AddVecVec(...) //*this += alpha * a * b^T
void AddMat(...) //*this += alpha * M
void AddMatMatMat(...) //this <-- beta*this + alpha*A*B*C.
void AddTpTp(...) //this <-- beta*this + alpha*A*B.
};