TensorFlow底層操作的數據結構是Tensor(張量),可以表示多維的數據,其實現在core/framework/tensor.h中,對於tensor的理解主要分兩大塊:
1.Tensor的組成成分
2.Tensor是如何進行數學運算的(TensorFlow本質就是處理大量訓練數據集,在底層要實現深度學習常用的算法,自然涉及到一些數學運算,例如:Tensor相加、相減,softmax,reduction等操作)
Tensor的組成:
Tensor的組成可以分成3部分:DataType、TensorShape、TensorBuf. 即數據類型、張量的形狀和存儲數據的內存.
DataType:Tensor數據的類型,float、double、int等
TensorShape: Tensor的形狀. 這里要表示維度需存儲兩種元素,第一種是維數、第二種是每個維度的大小. 例如:一個矩陣的大小為3×4,那么其維數為2,第一個維度大小是3,第二個是4.
TensorBuf:就是存儲數據的內存地址,比如一個Tensor是3×4的矩陣,並且類型時float,那么其TensorBuf就float*類型,並且長度是12,在實現上是基於模板實現的.
Tensor的運算:
Tensor的運算主要由kernels完成,每個kernel都完成不同的運算。其中CPU版本的kernel大部分都使用Eigen庫來實現,GPU版本的kernel少部分使用Eigen,大部分使用cuda編程實現。
Eigen unsupported模塊提供了同樣叫Tensor的類,主要就是能完成各種復雜的數學計算,並且是並行實現的(主要使用線程池或cuda),效率很高. 其API文檔可參考 https://github.com/PX4/eigen/blob/master/unsupported/Eigen/CXX11/src/Tensor/README.md. TensorFlow使用Eigen進行計算時,首先要將其自己實現的Tensor類的對象轉換成Eigen支持的Tensor對象. 可以再core/framework/tensor_types.h下面看到Eigen支持的各種數據類型,主要是TensorMap類. 並進行了重命名,其源碼如下所示:
struct TTypes {
// Rank-<NDIMS> tensor of scalar type T.
typedef Eigen::TensorMap<Eigen::Tensor<T, NDIMS, Eigen::RowMajor, IndexType>,
Eigen::Aligned>
Tensor;
typedef Eigen::TensorMap<
Eigen::Tensor<const T, NDIMS, Eigen::RowMajor, IndexType>, Eigen::Aligned>
ConstTensor;
// Unaligned Rank-<NDIMS> tensor of scalar type T.
typedef Eigen::TensorMap<Eigen::Tensor<T, NDIMS, Eigen::RowMajor, IndexType> >
UnalignedTensor;
typedef Eigen::TensorMap<
Eigen::Tensor<const T, NDIMS, Eigen::RowMajor, IndexType> >
UnalignedConstTensor;
typedef Eigen::TensorMap<Eigen::Tensor<T, NDIMS, Eigen::RowMajor, int>,
Eigen::Aligned>
Tensor32Bit;
// Scalar tensor (implemented as a rank-0 tensor) of scalar type T.
typedef Eigen::TensorMap<
Eigen::TensorFixedSize<T, Eigen::Sizes<>, Eigen::RowMajor, IndexType>,
Eigen::Aligned>
Scalar;
typedef Eigen::TensorMap<Eigen::TensorFixedSize<const T, Eigen::Sizes<>,
Eigen::RowMajor, IndexType>,
Eigen::Aligned>
ConstScalar;
// Unaligned Scalar tensor of scalar type T.
typedef Eigen::TensorMap<
Eigen::TensorFixedSize<T, Eigen::Sizes<>, Eigen::RowMajor, IndexType> >
UnalignedScalar;
typedef Eigen::TensorMap<Eigen::TensorFixedSize<const T, Eigen::Sizes<>,
Eigen::RowMajor, IndexType> >
UnalignedConstScalar;
// Rank-1 tensor (vector) of scalar type T.
typedef Eigen::TensorMap<Eigen::Tensor<T, 1, Eigen::RowMajor, IndexType>,
Eigen::Aligned>
Flat;
typedef Eigen::TensorMap<
Eigen::Tensor<const T, 1, Eigen::RowMajor, IndexType>, Eigen::Aligned>
ConstFlat;
typedef Eigen::TensorMap<Eigen::Tensor<T, 1, Eigen::RowMajor, IndexType>,
Eigen::Aligned>
Vec;
typedef Eigen::TensorMap<
Eigen::Tensor<const T, 1, Eigen::RowMajor, IndexType>, Eigen::Aligned>
ConstVec;
// Unaligned Rank-1 tensor (vector) of scalar type T.
typedef Eigen::TensorMap<Eigen::Tensor<T, 1, Eigen::RowMajor, IndexType> >
UnalignedFlat;
typedef Eigen::TensorMap<
Eigen::Tensor<const T, 1, Eigen::RowMajor, IndexType> >
UnalignedConstFlat;
typedef Eigen::TensorMap<Eigen::Tensor<T, 1, Eigen::RowMajor, IndexType> >
UnalignedVec;
typedef Eigen::TensorMap<
Eigen::Tensor<const T, 1, Eigen::RowMajor, IndexType> >
UnalignedConstVec;
// Rank-2 tensor (matrix) of scalar type T.
typedef Eigen::TensorMap<Eigen::Tensor<T, 2, Eigen::RowMajor, IndexType>,
Eigen::Aligned>
Matrix;
typedef Eigen::TensorMap<
Eigen::Tensor<const T, 2, Eigen::RowMajor, IndexType>, Eigen::Aligned>
ConstMatrix;
// Unaligned Rank-2 tensor (matrix) of scalar type T.
typedef Eigen::TensorMap<Eigen::Tensor<T, 2, Eigen::RowMajor, IndexType> >
UnalignedMatrix;
typedef Eigen::TensorMap<
Eigen::Tensor<const T, 2, Eigen::RowMajor, IndexType> >
UnalignedConstMatrix;
};
在tensor.h可以看到一些轉換函數,比如:flat(拉伸)、vec(向量化)、matrix(矩陣化)等。部分源碼如下所示:
template <typename T>
typename TTypes<T>::Vec vec() {
return tensor<T, 1>();
}
template <typename T>
typename TTypes<T>::Matrix matrix() {
return tensor<T, 2>();
}
template <typename T, size_t NDIMS>
typename TTypes<T, NDIMS>::Tensor tensor();
template <typename T>
typename TTypes<T>::Flat flat() {
return shaped<T, 1>({NumElements()});
}
調用這些函數就能將tensor轉換成Eigen支持的類型,就可以直接使用Eigen提供的API進行計算. 像矩陣乘、二元操作、一元操作等都是通過調用Eigen實現的. 所以只要了解了Eigen的基本數據類型,就能輕松看懂tensor這部分源碼. 了解了Eigen操作基本類型的API就能看懂kernels的代碼.
注:這里為什么不直接用Eigen::Tensor?原因是Eigen::Tensor只提供了最基本的數學運算,而有時候我們在運算的時候要進行不同程度的預處理,可以參考源碼中的cwise_ops_common.h中的
