forked from mindspore-Ecosystem/mindspore
kronecker production decomposition to maxtrix
This commit is contained in:
parent
03eff82849
commit
95c9c54e38
|
@ -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();
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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];
|
||||
|
|
|
@ -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_
|
||||
|
|
Loading…
Reference in New Issue