kronecker production decomposition to maxtrix

This commit is contained in:
lz 2022-08-23 11:13:54 +08:00
parent 03eff82849
commit 95c9c54e38
5 changed files with 282 additions and 42 deletions

View File

@ -55,7 +55,7 @@ TEST_F(SVDTest, TestSVD) {
}
SVDMatrix svd(data.data(), data.size(), shape);
int status = svd.DoSVDWithRank(3);
int status = svd.CompressWithRank(3);
ASSERT_EQ(status, RET_OK);
std::vector<float> mat_a_rank = svd.GetMatA();
std::vector<float> mat_b_rank = svd.GetMatB();
@ -67,7 +67,7 @@ TEST_F(SVDTest, TestSVD) {
ASSERT_EQ(mat_a_rank.size(), 21);
ASSERT_EQ(mat_b_rank.size(), 15);
status = svd.DoSVDWithErr(5);
status = svd.CompressWithFNorm(5);
ASSERT_EQ(status, RET_OK);
std::vector<float> mat_a_err = svd.GetMatA();
std::vector<float> mat_b_err = svd.GetMatB();

View File

@ -0,0 +1,168 @@
/**
* Copyright 2022 Huawei Technologies Co., Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "tools/converter/decomposer/kp_decomposition.h"
#include <set>
#include <vector>
#include <cmath>
#include <utility>
#include "src/common/log_util.h"
#include "src/common/log_adapter.h"
#include "include/errorcode.h"
#include "tools/converter/decomposer/svd_matrix.h"
namespace mindspore::lite::decomposer {
namespace {
constexpr int kRowIndex = 0;
constexpr int kColIndex = 1;
constexpr int kMinSize = 2;
} // namespace
// Integer factorization to multiply two constraints ==> A = B * C
std::set<std::pair<int, int>> KPDecomposition::IntegerFactorization(int num) {
std::set<std::pair<int, int>> result;
for (size_t i = 2; i <= std::sqrt(num); ++i) {
if (num % i == 0) {
result.insert({i, num / i});
result.insert({num / i, i});
}
}
return result;
}
// Find factors with the maximum compression ratio.
size_t KPDecomposition::FindBestFactorization(const std::set<std::pair<int, int>> &m_factors,
const std::set<std::pair<int, int>> &n_factors, size_t origin_element_num,
std::pair<int, int> *best_pair_m, std::pair<int, int> *best_pair_n) {
float best_compression = 1.0f;
for (auto m : m_factors) {
for (auto n : n_factors) {
// B = M1 * N1
auto element_m = m.first * n.first;
// C = M2 * N2
auto element_n = n.second * n.second;
float compression = 1.0f * origin_element_num / element_m + element_n;
if (compression > best_compression) {
best_compression = compression;
*best_pair_m = m;
*best_pair_n = n;
}
}
}
return best_compression;
}
int KPDecomposition::PackMatrixA(const float *data, size_t num, const std::vector<int> &shape,
const std::vector<int> &block_size, float *new_data) {
auto rows = shape[kRowIndex];
auto cols = shape[kColIndex];
auto stride_row = rows / block_size.at(kRowIndex);
auto stride_col = cols / block_size.at(kColIndex);
size_t new_col = stride_col * stride_row;
size_t new_row = block_size.at(kColIndex) * block_size.at(kRowIndex);
if (new_col * new_row != num) {
return RET_ERROR;
}
size_t total_index = 0;
for (int col = 0; col < block_size.at(kColIndex); ++col) {
for (int row = 0; row < block_size.at(kRowIndex); ++row) {
auto start = row * (stride_row * cols) + stride_col * col;
for (int c = 0; c < stride_col; ++c) {
for (int r = 0; r < stride_row; ++r) {
// rows => cols
auto index = start + r * cols + c;
// `index` and `total_index` are guaranteed to be less than `num`
MS_ASSERT(index < num);
MS_ASSERT(total_index < num);
new_data[total_index++] = data[index];
}
}
}
}
return RET_OK;
}
double KPDecomposition::CalcFNorm(
const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> &matrix_b,
const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> &matrix_c) {
auto kp = kroneckerProduct(matrix_b, matrix_c).eval();
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> origin_matrix =
Eigen::Map<Eigen::VectorXf, Eigen::Unaligned>(data_, data_size_);
origin_matrix.resize(shape_.at(kRowIndex), shape_.at(kColIndex));
auto f_norm = (origin_matrix - kp).norm();
return f_norm;
}
int KPDecomposition::Decomposition() {
if (shape_.size() != kMinSize) {
MS_LOG(ERROR) << "shape size only support 2, but get " << shape_.size();
return RET_ERROR;
}
auto m_factors = IntegerFactorization(shape_.at(kRowIndex));
auto n_factors = IntegerFactorization(shape_.at(kColIndex));
std::pair<int, int> best_pair_m;
std::pair<int, int> best_pair_n;
auto ratio = FindBestFactorization(m_factors, n_factors, data_size_, &best_pair_m, &best_pair_n);
MS_LOG(INFO) << "M is [" << best_pair_m.first << "," << best_pair_m.second << "] and N is [" << best_pair_n.first
<< "," << best_pair_n.second << "], kp compression ratio is " << ratio;
if (ratio <= 1) {
MS_LOG(WARNING) << "compression ratio " << ratio << " <= 1.";
return RET_NO_CHANGE;
}
auto *pack_matrix_a = static_cast<float *>(malloc(data_size_ * sizeof(float)));
CHECK_MALLOC_RES(pack_matrix_a, RET_ERROR);
// Matrix A shape is (B_element, C_element)
std::vector<int> pack_matrix_shapes{{best_pair_m.first * best_pair_n.first, best_pair_m.second * best_pair_n.second}};
mat_shape_b_ = {best_pair_m.first, best_pair_n.first};
mat_shape_c_ = {best_pair_m.second, best_pair_n.second};
auto ret = PackMatrixA(data_, data_size_, shape_, mat_shape_b_, pack_matrix_a);
if (ret != RET_OK) {
MS_LOG(ERROR) << "Pack MatrixA failed.";
free(pack_matrix_a);
return ret;
}
auto svd = SVDMatrix(pack_matrix_a, data_size_, pack_matrix_shapes);
ret = svd.Decomposition();
free(pack_matrix_a);
if (ret != RET_OK) {
MS_LOG(ERROR) << "SVD decomposition failed.";
return ret;
}
auto sqrt_sigma0 = std::sqrt(svd.GetSigma().data()[0]);
// vec(B)
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> matrix_b =
sqrt_sigma0 * svd.GetU().col(0).eval();
// vec(C)
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> matrix_c =
sqrt_sigma0 * svd.GetV().col(0).eval();
// Matrix(B)
matrix_b = matrix_b.reshaped(mat_shape_b_.at(kRowIndex), mat_shape_b_.at(kColIndex)).eval();
// Matrix(C)
matrix_c = matrix_c.reshaped(mat_shape_c_.at(kRowIndex), mat_shape_c_.at(kColIndex)).eval();
mat_b_.assign(matrix_b.data(), matrix_b.data() + mat_shape_b_.at(kRowIndex) * mat_shape_b_.at(kColIndex));
mat_c_.assign(matrix_c.data(), matrix_c.data() + mat_shape_c_.at(kRowIndex) * mat_shape_c_.at(kColIndex));
auto f_norm = CalcFNorm(matrix_b, matrix_c);
MS_LOG(INFO) << "Calc FNorm is " << f_norm;
return RET_OK;
}
} // namespace mindspore::lite::decomposer

View File

@ -0,0 +1,63 @@
/**
* Copyright 2022 Huawei Technologies Co., Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MINDSPORE_LITE_TOOLS_CONVERTER_DECOMPOSER_KRONECKER_DECOMPOSITION_H_
#define MINDSPORE_LITE_TOOLS_CONVERTER_DECOMPOSER_KRONECKER_DECOMPOSITION_H_
#include <set>
#include <vector>
#include <utility>
#include "unsupported/Eigen/KroneckerProduct"
namespace mindspore::lite::decomposer {
// Kronecker Production Decomposition to matrix
class KPDecomposition {
public:
KPDecomposition(float *data, size_t data_size, const std::vector<int> &shape)
: data_(data), data_size_(data_size), shape_(shape) {}
~KPDecomposition() = default;
int Decomposition();
std::vector<float> GetMatB() { return mat_b_; }
std::vector<int> GetMatShapeB() { return mat_shape_b_; }
std::vector<float> GetMatC() { return mat_c_; }
std::vector<int> GetMatShapeC() { return mat_shape_c_; }
private:
// Integer factorization to multiply two constraints ==> A = B * C
std::set<std::pair<int, int>> IntegerFactorization(int num);
size_t FindBestFactorization(const std::set<std::pair<int, int>> &m_factors,
const std::set<std::pair<int, int>> &n_factors, size_t origin_element_num,
std::pair<int, int> *best_pair_m, std::pair<int, int> *best_pair_n);
int PackMatrixA(const float *data, size_t num, const std::vector<int> &shape, const std::vector<int> &block_size,
float *new_data);
double CalcFNorm(const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> &matrix_b,
const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> &matrix_c);
private:
float *data_ = nullptr;
size_t data_size_ = 0;
std::vector<int> shape_;
std::vector<float> mat_b_;
std::vector<int> mat_shape_b_;
std::vector<float> mat_c_;
std::vector<int> mat_shape_c_;
};
} // namespace mindspore::lite::decomposer
#endif // MINDSPORE_LITE_TOOLS_CONVERTER_DECOMPOSER_KRONECKER_DECOMPOSITION_H_

View File

@ -24,8 +24,9 @@ namespace mindspore::lite::decomposer {
namespace {
constexpr int kRowIndex = 0;
constexpr int kColIndex = 1;
constexpr int kMinSize = 2;
} // namespace
int SVDMatrix::DoSVDWithRank(int rank) {
int SVDMatrix::CompressWithRank(int rank) {
if (rank <= 0) {
MS_LOG(ERROR) << " The rank = " << rank << ", it is too small";
return RET_ERROR;
@ -41,51 +42,47 @@ int SVDMatrix::DoSVDWithRank(int rank) {
mat_a_.clear();
mat_b_.clear();
SVDCompress();
Decomposition();
TruncateSVD(svd_);
return RET_OK;
}
int SVDMatrix::DoSVDWithErr(float err) {
int SVDMatrix::Decomposition() {
if (shape_.size() != kMinSize) {
MS_LOG(ERROR) << "shape size only support 2, but get " << shape_.size();
return RET_ERROR;
}
int row = shape_[kRowIndex];
int col = shape_[kColIndex];
// Convert std::vector to Eigen::MatrixXf
origin_matrix_ = Eigen::Map<Eigen::VectorXf, Eigen::Unaligned>(data_.data(), data_.size()).eval();
origin_matrix_.resize(row, col);
// Singular Value Decomposition
svd_ = Eigen::BDCSVD<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(
origin_matrix_, Eigen::ComputeThinU | Eigen::ComputeThinV);
U_ = svd_.matrixU();
V_ = svd_.matrixV();
Sigma_ = svd_.singularValues().asDiagonal();
return RET_OK;
}
int SVDMatrix::CompressWithFNorm(float f_norm) {
// clear mat
mat_a_.clear();
mat_b_.clear();
int row = shape_[kRowIndex];
int col = shape_[kColIndex];
// Convert std::vector to Eigen::MatrixXf
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> orgin_matrix =
Eigen::Map<Eigen::VectorXf, Eigen::Unaligned>(data_.data(), data_.size());
orgin_matrix.resize(row, col);
// Singular Value Decomposition
Eigen::BDCSVD<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> svd(
orgin_matrix, Eigen::ComputeThinU | Eigen::ComputeThinV);
if (GetBestRank(orgin_matrix, svd, err) == RET_ERROR) {
Decomposition();
if (GetBestRank(origin_matrix_, svd_, f_norm) == RET_ERROR) {
return RET_ERROR;
}
TruncateSVD(svd);
TruncateSVD(svd_);
return RET_OK;
}
void SVDMatrix::SVDCompress() {
int row = shape_[kRowIndex];
int col = shape_[kColIndex];
// Convert std::vector to Eigen::MatrixXf
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> orgin_matrix =
Eigen::Map<Eigen::VectorXf, Eigen::Unaligned>(data_.data(), data_.size());
orgin_matrix.resize(row, col);
// Singular Value Decomposition
Eigen::BDCSVD<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> svd(
orgin_matrix, Eigen::ComputeThinU | Eigen::ComputeThinV);
TruncateSVD(svd);
}
void SVDMatrix::TruncateSVD(
const Eigen::BDCSVD<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> &svd) {
int row = shape_[kRowIndex];

View File

@ -23,28 +23,34 @@
namespace mindspore::lite::decomposer {
class SVDMatrix {
public:
SVDMatrix(const float *data_ptr, size_t data_size, const std::vector<int> shape)
SVDMatrix(const float *data_ptr, size_t data_size, const std::vector<int> &shape)
: data_(data_ptr, data_ptr + data_size), shape_(shape) {}
~SVDMatrix() = default;
int DoSVDWithRank(int rank);
// err means matrix Frobenius Norm
int DoSVDWithErr(float err);
int Decomposition();
int CompressWithRank(int rank);
// f_norm means matrix Frobenius Norm
int CompressWithFNorm(float f_norm);
std::vector<float> GetMatA() { return mat_a_; }
std::vector<int> GetMatShapeA() { return mat_shape_a_; }
std::vector<float> GetMatB() { return mat_b_; }
std::vector<int> GetMatShapeB() { return mat_shape_b_; }
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> GetU() { return U_; }
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> GetV() { return V_; }
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> GetSigma() { return Sigma_; }
private:
void SVDCompress();
int GetBestRank(const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> &orgin_matrix,
const Eigen::BDCSVD<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> &svd,
float err);
void TruncateSVD(const Eigen::BDCSVD<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> &svd);
float ComputeReduceMemoryRatio(const int rank);
float ComputeReduceMemoryRatio(int rank);
private:
std::vector<float> data_;
std::vector<int> shape_;
std::vector<float> mat_a_;
@ -52,6 +58,12 @@ class SVDMatrix {
std::vector<float> mat_b_;
std::vector<int> mat_shape_b_;
int rank_ = -1;
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> origin_matrix_;
Eigen::BDCSVD<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> svd_;
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> U_;
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> V_;
Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> Sigma_;
};
} // namespace mindspore::lite::decomposer
#endif // MINDSPORE_LITE_TOOLS_CONVERTER_DECOMPOSER_SVD_MATRIX_H_