forked from huawei/mindspore2022
Chen's pr #2
|
|
@ -31,6 +31,7 @@ class CallbackImpl;
|
|||
|
||||
using GraphPoint = std::pair<int, float>;
|
||||
|
||||
/// The TrainCallBackData class defines a set of parameters for training callbacks.
|
||||
struct TrainCallBackData {
|
||||
TrainCallBackData(bool train_mode, int epoch, int step, Model *model): train_mode_(train_mode), epoch_(epoch),
|
||||
step_(step), model_(model) {}
|
||||
|
|
@ -41,6 +42,7 @@ struct TrainCallBackData {
|
|||
Model *model_; /**< pointer to the Model object */
|
||||
};
|
||||
|
||||
/// The CallbackRetValue class represents whether to continue looping in training.
|
||||
enum CallbackRetValue : uint32_t {
|
||||
kContinue = 0,
|
||||
kStopTraining = 1,
|
||||
|
|
@ -48,6 +50,8 @@ enum CallbackRetValue : uint32_t {
|
|||
kUnknownRetValue = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
/// TrainCallBack is the training callback class in MindSpore Lite.
|
||||
/// In MindSpore, the callback function is actually not a function but a class.
|
||||
class TrainCallBack {
|
||||
public:
|
||||
virtual ~TrainCallBack() = default;
|
||||
|
|
@ -88,6 +92,8 @@ class TrainCallBack {
|
|||
virtual void StepEnd(const TrainCallBackData &cb_data) {}
|
||||
|
||||
protected:
|
||||
/// Due to TrainCallBack being used for implementing callbacks during the training process, the Model class and ModelImpl class need to access its private methods.
|
||||
/// This is achieved through the use of friend classes.
|
||||
friend class Model;
|
||||
friend class ModelImpl;
|
||||
CallbackImpl* callback_impl_ = nullptr;
|
||||
|
|
|
|||
|
|
@ -20,20 +20,30 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
/// The "callback.h" provides the class TrainCallBack, which is responsible for generating its subclass CkptSaver.
|
||||
#include "include/api/callback/callback.h"
|
||||
/// The "dual_abi_helper.h" provides the StringToChar method, which converts data of type std::string& to std::vector<char>& .
|
||||
#include "include/api/dual_abi_helper.h"
|
||||
|
||||
namespace mindspore {
|
||||
|
||||
/// \brief The CkptSaver class is the model file saving class of MindSpore Lite for training.
|
||||
/// The CkptSaver class is a subclass of TrainCallBack.
|
||||
class CkptSaver: public TrainCallBack {
|
||||
public:
|
||||
inline CkptSaver(int save_every_n, const std::string &filename_prefix);
|
||||
virtual ~CkptSaver();
|
||||
|
||||
private:
|
||||
/// \brief The private constructor of CkptSaver is used for initialization by the public constructor of CkptSaver.
|
||||
/// \param save_every_n The step size of the callback.
|
||||
/// \param filename_prefix The prefix of a file name.
|
||||
CkptSaver(int save_every_n, const std::vector<char> &filename_prefix);
|
||||
};
|
||||
|
||||
/// \brief The public constructor use a private constructor to initialize CkptSaver to ensure data security.
|
||||
/// \param save_every_n The step size of the callback.
|
||||
/// \param filename_prefix The prefix of a file name.
|
||||
CkptSaver::CkptSaver(int save_every_n, const std::string &filename_prefix)
|
||||
: CkptSaver(save_every_n, StringToChar(filename_prefix)) {}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,10 +23,20 @@
|
|||
|
||||
namespace mindspore {
|
||||
|
||||
/// \brief LossMonitor is a class for training loss functions in MindSpore Lite.
|
||||
/// LossMonitor is a subclass of TrainCallBack, and the inheritance method is public inheritance.
|
||||
class LossMonitor: public TrainCallBack {
|
||||
public:
|
||||
|
||||
/// \brief The single-parameter constructor of LossMonitor is decorated with the "explicit" keyword to ensure that it cannot be implicitly converted.
|
||||
/// \param print_every_n_steps is the callback step size for displaying the loss function value Loss.
|
||||
explicit LossMonitor(int print_every_n_steps = INT_MAX);
|
||||
|
||||
/// \brief Virtual Destructor of LossMonitor.
|
||||
virtual ~LossMonitor();
|
||||
|
||||
/// @brief The GetLossPoints method is used to obtain training loss data.
|
||||
/// @return a vector containing GraphPoint data representing the training loss data.
|
||||
const std::vector<GraphPoint> &GetLossPoints();
|
||||
};
|
||||
} // namespace mindspore
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ int MultiplicativeLRLambda(float *lr, int epoch, void *multiplication);
|
|||
|
||||
/// \brief Multiply the LR by a factor of gamma every step_size
|
||||
int StepLRLambda(float *lr, int epoch, void *step_size);
|
||||
|
||||
/// \brief StepLRLambda is a struct that defines a set of parameters for training learning rate.
|
||||
struct StepLRLambda {
|
||||
StepLRLambda(int step, float g) : step_size(step), gamma(g) {}
|
||||
|
||||
|
|
@ -41,8 +43,14 @@ struct StepLRLambda {
|
|||
float gamma; // LR decay factor
|
||||
};
|
||||
|
||||
/// \brief LRScheduler is the training learning rate scheduling class of MindSpore Lite.
|
||||
class LRScheduler: public TrainCallBack {
|
||||
public:
|
||||
|
||||
/// \brief Constructor of LRScheduler.
|
||||
/// \param lambda_func The lambda_func is a function wrapper template that wraps a function. It is used to adjust the learning rate during training.
|
||||
/// \param lr_cb_data Data parameter during the callback process.
|
||||
/// \param step is the callback step size for learning rate changes. The default value is 1.
|
||||
explicit LRScheduler(LR_Lambda lambda_func, void *lr_cb_data = nullptr, int step = 1);
|
||||
virtual ~LRScheduler();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,10 +24,18 @@
|
|||
|
||||
namespace mindspore {
|
||||
|
||||
/// \brief TimeMonitor is a training time monitoring class in MindSpore Lite.
|
||||
class TimeMonitor: public TrainCallBack {
|
||||
public:
|
||||
virtual ~TimeMonitor() = default;
|
||||
|
||||
/// \brief The EpochBegin method is called before each iteration, overriding the method of the parent class.
|
||||
/// \param cb_data include a set of parameters for training callbacks.
|
||||
void EpochBegin(const TrainCallBackData &cb_data) override;
|
||||
|
||||
/// \brief The EpochEnd method is called after each round of iteration, overriding the method of the parent class.
|
||||
/// \param cb_data include a set of parameters for training callbacks.
|
||||
/// \return CallbackRetValue, indicating whether to continue looping in training.
|
||||
CallbackRetValue EpochEnd(const TrainCallBackData &cb_data) override;
|
||||
};
|
||||
} // namespace mindspore
|
||||
|
|
|
|||
|
|
@ -26,13 +26,23 @@
|
|||
|
||||
namespace mindspore {
|
||||
|
||||
/// \brief TrainAccuracy is a learning rate scheduling class in MindSpore Lite for training.
|
||||
class TrainAccuracy: public TrainCallBack {
|
||||
public:
|
||||
|
||||
/// \brief Constructor of TrainAccuracy.
|
||||
/// \param print_every_n is the callback step size.
|
||||
/// \param accuracy_metrics is the accuracy metric, with a default value of METRICS_CLASSIFICATION indicating 0.
|
||||
/// \param input_indexes is the index of input.
|
||||
/// \param output_indexes is the index of output.
|
||||
explicit TrainAccuracy(int print_every_n = INT_MAX,
|
||||
int accuracy_metrics = METRICS_CLASSIFICATION,
|
||||
const std::vector<int> &input_indexes = {1},
|
||||
const std::vector<int> &output_indexes = {0});
|
||||
virtual ~TrainAccuracy();
|
||||
|
||||
/// \brief The GetAccuracyPoints method is used to obtain the training accuracy.
|
||||
/// \return Return a vector containing GraphPoint, i.e. training accuracy data.
|
||||
const std::vector<GraphPoint> &GetAccuracyPoints();
|
||||
};
|
||||
} // namespace mindspore
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ class Context;
|
|||
using Input = InputAndOutput;
|
||||
using Output = InputAndOutput;
|
||||
|
||||
|
||||
/// \brief Container base class.
|
||||
class MS_API CellBase {
|
||||
public:
|
||||
CellBase() = default;
|
||||
|
|
@ -39,13 +41,20 @@ class MS_API CellBase {
|
|||
std::vector<Output> operator()(const std::vector<Input> &inputs) const;
|
||||
};
|
||||
|
||||
/// \brief Container class.
|
||||
/// \brief A cell is the base class for all neural networks. A cell can be a single neural network unit or a unit that forms a network.
|
||||
template <class T>
|
||||
class MS_API Cell : public CellBase {
|
||||
public:
|
||||
virtual ~Cell() = default;
|
||||
/// \brief The Clone method is used to create a copy of itself.
|
||||
/// \return a pointer to a copy.
|
||||
std::shared_ptr<CellBase> Clone() const override { return std::make_shared<T>(static_cast<const T &>(*this)); }
|
||||
};
|
||||
|
||||
/// \brief Graph container class.
|
||||
/// Use GraphCell to run the computation graph loaded from MindIR.
|
||||
/// In GRAPH_MODE (static graph mode), Cell will be compiled into a computational graph, while in PYNATIVE_MODE (dynamic graph mode), it serves as the foundational module for neural networks.
|
||||
class MS_API GraphCell final : public Cell<GraphCell> {
|
||||
public:
|
||||
class GraphImpl;
|
||||
|
|
@ -53,38 +62,60 @@ class MS_API GraphCell final : public Cell<GraphCell> {
|
|||
GraphCell() = default;
|
||||
~GraphCell() override = default;
|
||||
|
||||
/// \brief Constructor of GraphCell.
|
||||
/// \param A pointer to a compiled graph loaded from MindIR.
|
||||
explicit GraphCell(const Graph &);
|
||||
explicit GraphCell(Graph &&);
|
||||
explicit GraphCell(const std::shared_ptr<Graph> &);
|
||||
|
||||
/// \brief Create graph impl for device target(set the Graph object and the executor context).
|
||||
/// \param context is the environment variable during execution.
|
||||
void SetContext(const std::shared_ptr<Context> &context);
|
||||
|
||||
/// \brief The GetGraph method is used to obtain a pointer to the graph object.
|
||||
/// \return a std::shared_ptr<Graph> & pointer of Graph.
|
||||
const std::shared_ptr<Graph> &GetGraph() const { return graph_; }
|
||||
|
||||
/// \brief A method to set the Graph object with member graph_ and to run the Graph with parameters inputs and outputs.
|
||||
/// \param inputs is a pointer of the input MStensor for the GraphCell.
|
||||
/// \param outputs is a pointer of the output MStensor for the GraphCell.
|
||||
/// \return the status of Running.
|
||||
Status Run(const std::vector<MSTensor> &inputs, std::vector<MSTensor> *outputs) override;
|
||||
|
||||
/// \brief Get the input MStensor for the GraphCell.
|
||||
/// \return the input MStensor for the GraphCell.
|
||||
std::vector<MSTensor> GetInputs();
|
||||
|
||||
/// \brief Get the output MStensor for the GraphCell.
|
||||
/// \return the output MStensor for the GraphCell.
|
||||
std::vector<MSTensor> GetOutputs();
|
||||
|
||||
/// \brief Load the object into the device of device_id.
|
||||
/// \param device_id is the target device ID to perform operation.
|
||||
/// \return a Status object of the StatusCode class, and you can use its public functions StatusCode or ToString to obtain the specific error code and error message.
|
||||
Status Load(uint32_t device_id);
|
||||
|
||||
private:
|
||||
friend class Model;
|
||||
friend class Model; /// The Model class can call all the methods of the Cell::GraphCell class.
|
||||
|
||||
std::shared_ptr<Graph> graph_;
|
||||
std::shared_ptr<GraphImpl> executor_;
|
||||
std::shared_ptr<Graph> graph_; /// graph_ is a pointer to a Graph object.
|
||||
std::shared_ptr<GraphImpl> executor_; /// executor_ is a pointer to the GraphImpl object.
|
||||
};
|
||||
|
||||
class MS_API InputAndOutput {
|
||||
public:
|
||||
InputAndOutput();
|
||||
~InputAndOutput() = default;
|
||||
|
||||
|
||||
InputAndOutput(const std::shared_ptr<CellBase> &, const std::vector<InputAndOutput> &, int32_t index);
|
||||
|
||||
int32_t GetIndex() const { return index_; }
|
||||
void SetIndex(int32_t index) { index_ = index; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<CellBase> cell_;
|
||||
std::vector<InputAndOutput> prev_;
|
||||
int32_t index_;
|
||||
std::shared_ptr<CellBase> cell_; /// cell_ is a pointer to a CellBase object.
|
||||
std::vector<InputAndOutput> prev_; /// prev_ is a pointer to an InputAndOutput object.
|
||||
int32_t index_;/// The index of the current InputAndOutput object.
|
||||
};
|
||||
} // namespace mindspore
|
||||
#endif // MINDSPORE_INCLUDE_API_CELL_H
|
||||
|
|
|
|||
|
|
@ -27,20 +27,30 @@ namespace mindspore {
|
|||
class MS_API Graph {
|
||||
public:
|
||||
class GraphData;
|
||||
/// \brief The default constructor of Graph will initialize the private member graph_data_ with nullptr.
|
||||
Graph();
|
||||
explicit Graph(const std::shared_ptr<GraphData> &graph_data);
|
||||
explicit Graph(std::shared_ptr<GraphData> &&graph_data);
|
||||
explicit Graph(std::nullptr_t);
|
||||
~Graph();
|
||||
|
||||
/// \brief The ModelType method is used to obtain the model type.
|
||||
/// \return Model Types.
|
||||
/// Due to the possibility that the Graph object(this pointer) may be constant, and C++ rules dictate that only pointers to constants can be used to store the address of constant objects, the const keyword is used to modify the this pointer.
|
||||
enum ModelType ModelType() const;
|
||||
|
||||
/// \brief Overloaded operator method for checking if it is a null pointer.
|
||||
/// Due to the possibility that the Graph object(this pointer) may be constant, and C++ rules dictate that only pointers to constants can be used to store the address of constant objects, the const keyword is used to modify the this pointer.
|
||||
bool operator==(std::nullptr_t) const;
|
||||
|
||||
/// \brief Operator Overloading Method for checking if it is a non-null pointer.
|
||||
/// Due to the possibility that the Graph object(this pointer) may be constant, and C++ rules dictate that only pointers to constants can be used to store the address of constant objects, the const keyword is used to modify the this pointer.
|
||||
bool operator!=(std::nullptr_t) const;
|
||||
|
||||
private:
|
||||
friend class GraphCell;
|
||||
friend class ModelImpl;
|
||||
std::shared_ptr<GraphData> graph_data_;
|
||||
std::shared_ptr<GraphData> graph_data_; /// Pointer to graphical model.
|
||||
};
|
||||
} // namespace mindspore
|
||||
#endif // MINDSPORE_INCLUDE_API_GRAPH_H
|
||||
|
|
|
|||
|
|
@ -23,12 +23,19 @@ namespace mindspore {
|
|||
constexpr int METRICS_CLASSIFICATION = 0;
|
||||
constexpr int METRICS_MULTILABEL = 1;
|
||||
|
||||
/// \brief AccuracyMetrics is the training accuracy class of MindSpore Lite.
|
||||
/// AccuracyMetrics is a subclass of Metrics, which overrides the Clear method and Eval method of the Metrics class.
|
||||
class AccuracyMetrics : public Metrics {
|
||||
public:
|
||||
explicit AccuracyMetrics(int accuracy_metrics = METRICS_CLASSIFICATION, const std::vector<int> &input_indexes = {1},
|
||||
const std::vector<int> &output_indexes = {0});
|
||||
virtual ~AccuracyMetrics();
|
||||
|
||||
/// \brief The Clear method is used to reset the accuracy to zero.
|
||||
void Clear() override;
|
||||
|
||||
/// \brief The Eval method is used for model validation.
|
||||
/// \return Validation accuracy of the model with float type.
|
||||
float Eval() override;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/// Prevent multiple inclusion of header files
|
||||
#ifndef MINDSPORE_INCLUDE_API_METRICS_METRICS_H
|
||||
#define MINDSPORE_INCLUDE_API_METRICS_METRICS_H
|
||||
#include <vector>
|
||||
|
|
@ -24,12 +25,27 @@ class MetricsImpl;
|
|||
class ModelImpl;
|
||||
class MSTensor;
|
||||
|
||||
/// Metrics is a training metric class in MindSpore Lite.
|
||||
/// Metrics are indicators used to evaluate the performance of a model.
|
||||
class Metrics {
|
||||
public:
|
||||
virtual ~Metrics() = default;
|
||||
/// Virtual destructor
|
||||
virtual ~Metrics() = default;
|
||||
|
||||
/// The Clear method is used to reset the training metrics to zero.
|
||||
virtual void Clear() {}
|
||||
|
||||
/// The Eval method is used for the model validation process and returns the model validation accuracy as a float value.
|
||||
/// \return a floating-point number.
|
||||
/// Default return 0.0 .
|
||||
virtual float Eval() { return 0.0; }
|
||||
|
||||
/// The Update method implements the update of model input and output data.
|
||||
/// \param[in] inputs is a vector of MSTensor, which is the model input.
|
||||
/// \param[in] outputs is a vector of MSTensor, which are the outputs of the model.
|
||||
virtual void Update(std::vector<MSTensor *> inputs, std::vector<MSTensor *> outputs) {}
|
||||
|
||||
/// Since Metrics is the training metric class of MindSpore Lite, the Model class and ModelImpl class need to call its private methods, which are achieved through the friend class mechanism.
|
||||
protected:
|
||||
friend class Model;
|
||||
friend class ModelImpl;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@
|
|||
#endif
|
||||
|
||||
namespace mindspore {
|
||||
|
||||
/// \brief The available model files have different model types. The ModelType enumeration variable represents the type of model file.
|
||||
/// \brief When ModelType is 0, 1, 2, 3, 4, or 0xFFFFFFFF, the corresponding model types are kMindIR, kAIR, kOM, kONNX, kMindIR_Lite, and unknown type, respectively.
|
||||
enum ModelType : uint32_t {
|
||||
kMindIR = 0,
|
||||
kAIR = 1,
|
||||
|
|
@ -369,7 +372,12 @@ struct MSCallBackParam {
|
|||
using MSKernelCallBack = std::function<bool(const std::vector<MSTensor> &inputs, const std::vector<MSTensor> &outputs,
|
||||
const MSCallBackParam &opInfo)>;
|
||||
|
||||
/// \brief The CharVersion method calls the mindspore::lite::Version() method to obtain the version number of MindSpore Lite.
|
||||
/// \return the version as a string.
|
||||
std::vector<char> CharVersion();
|
||||
|
||||
/// \brief The Version method is used to retrieve the version number.
|
||||
/// \return the version of MindSpore Lite as a string.
|
||||
inline std::string Version() { return CharToString(CharVersion()); }
|
||||
|
||||
} // namespace mindspore
|
||||
|
|
|
|||
|
|
@ -28,8 +28,14 @@ Graph::~Graph() {}
|
|||
|
||||
Graph::Graph(std::nullptr_t) : graph_data_(nullptr) {}
|
||||
|
||||
/// \brief Overload the operator to determine if the graph model (the object pointed to by graph_data) fails to load.
|
||||
/// If it fails to load, graph_data_==nullptr, and this method returns True.
|
||||
/// Due to the possibility that the Graph object(this pointer) may be constant, and C++ rules dictate that only pointers to constants can be used to store the address of constant objects, the const keyword is used to modify the this pointer.
|
||||
bool Graph::operator==(std::nullptr_t) const { return graph_data_ == nullptr; }
|
||||
|
||||
/// \brief Overload the operator to determine whether the graph model (the object pointed to by graph_data) is loaded successfully.
|
||||
/// If it is loaded successfully, graph_data_ != nullptr, and this method returns True.
|
||||
/// Due to the possibility that the Graph object(this pointer) may be constant, and C++ rules dictate that only pointers to constants can be used to store the address of constant objects, the const keyword is used to modify the this pointer.
|
||||
bool Graph::operator!=(std::nullptr_t) const { return graph_data_ != nullptr; }
|
||||
|
||||
ModelType Graph::ModelType() const {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#include "ir/func_graph.h"
|
||||
|
||||
namespace mindspore {
|
||||
class Graph::GraphData {
|
||||
class Graph::GraphData {/// Define the inner class GraphData of Graph.
|
||||
public:
|
||||
GraphData();
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ struct PyDataToValueRegister {
|
|||
PyDataToValueRegister() { python_adapter::PyAdapterCallback::SetPyDataToValueHandler(data_converter::PyDataToValue); }
|
||||
} callback_register;
|
||||
} // namespace
|
||||
using Tensor = mindspore::tensor::Tensor;
|
||||
using Tensor = mindspore::tensor::Tensor; //导入变量
|
||||
using TensorPtr = mindspore::tensor::TensorPtr;
|
||||
using MetaTensor = mindspore::tensor::MetaTensor;
|
||||
using MetaTensorPtr = mindspore::tensor::MetaTensorPtr;
|
||||
|
|
@ -57,19 +57,19 @@ static constexpr int kBit16 = 16;
|
|||
static constexpr int kBit32 = 32;
|
||||
static constexpr int kBit64 = 64;
|
||||
|
||||
class DataConverter {
|
||||
class DataConverter { // 数据转换器
|
||||
public:
|
||||
explicit DataConverter(InstanceConvertFunc convert_func) : convert_func_(std::move(convert_func)) {}
|
||||
explicit DataConverter(InstanceConvertFunc convert_func) : convert_func_(std::move(convert_func)) {} //不允许隐式转换
|
||||
|
||||
virtual ~DataConverter() = default;
|
||||
virtual ~DataConverter() = default; // 虚析构函数
|
||||
|
||||
virtual bool Matched(const py::object &obj) = 0;
|
||||
|
||||
virtual ValuePtr ConvertPyObject(const py::object &obj, bool use_sig, const TypePtr &dtype) {
|
||||
if (convert_func_ == nullptr) {
|
||||
virtual ValuePtr ConvertPyObject(const py::object &obj, bool use_sig, const TypePtr &dtype) { //将python对象转换为valueptr
|
||||
if (convert_func_ == nullptr) { //如果转换函数为空,使用 MS_LOG 宏记录一条异常日志,并抛出异常。
|
||||
MS_LOG(EXCEPTION) << "convert func is null";
|
||||
}
|
||||
return convert_func_(obj, use_sig, dtype);
|
||||
return convert_func_(obj, use_sig, dtype); //正常调用转换函数
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -83,7 +83,7 @@ using ArgsObjSigConvertFunc = std::function<ValuePtr(const py::object &, bool)>;
|
|||
using ArgsOjbTypeConvertFunc = std::function<ValuePtr(const py::object &, const TypePtr &)>;
|
||||
|
||||
// Convert the data according instance type
|
||||
template <typename T>
|
||||
template <typename T> //模板类,通过不同的构造函数和类型检查函数,可以根据实例的类型匹配和执行不同的转换操作。
|
||||
class ByTypeDataConverter : public DataConverter {
|
||||
public:
|
||||
explicit ByTypeDataConverter(const InstanceConvertFunc &convert_func)
|
||||
|
|
@ -113,14 +113,14 @@ class ByTypeDataConverter : public DataConverter {
|
|||
|
||||
~ByTypeDataConverter() override = default;
|
||||
|
||||
bool Matched(const py::object &obj) override { return check_func_ != nullptr ? check_func_(obj) : false; }
|
||||
bool Matched(const py::object &obj) override { return check_func_ != nullptr ? check_func_(obj) : false; } //用于检查传入的 Python 对象是否与该类型匹配
|
||||
|
||||
private:
|
||||
InstanceCheckFunc check_func_ = nullptr;
|
||||
};
|
||||
|
||||
// Convert the data according object attribute.
|
||||
class ByAttrDataConverter : public DataConverter {
|
||||
class ByAttrDataConverter : public DataConverter { //通过不同的构造函数和属性检查函数,可以根据对象的属性进行匹配和执行不同的转换操作。
|
||||
public:
|
||||
ByAttrDataConverter(const std::string &attr_name, const ArgsObjConvertFunc &convert_func)
|
||||
: DataConverter(
|
||||
|
|
@ -135,40 +135,40 @@ class ByAttrDataConverter : public DataConverter {
|
|||
|
||||
~ByAttrDataConverter() override = default;
|
||||
|
||||
bool Matched(const py::object &obj) override { return py::hasattr(obj, attr_name_.c_str()); }
|
||||
bool Matched(const py::object &obj) override { return py::hasattr(obj, attr_name_.c_str()); } //用于检查传入的 Python 对象是否具有指定的属性。
|
||||
|
||||
private:
|
||||
std::string attr_name_;
|
||||
};
|
||||
|
||||
FuncGraphPtr ConvertToBpropCut(const py::object &obj) {
|
||||
std::vector<std::string> results = data_converter::GetObjKey(obj);
|
||||
FuncGraphPtr ConvertToBpropCut(const py::object &obj) { //将给定的 Python 对象转换为一个反向传播截断的 FuncGraphPtr 对象
|
||||
std::vector<std::string> results = data_converter::GetObjKey(obj); //获取对象的关键字,将传入的 Python 对象转换为一个字符串数组
|
||||
std::string obj_key = results[0];
|
||||
py::function bprop_func = py::getattr(obj, CUSTOM_BPROP_NAME);
|
||||
py::function bprop_func = py::getattr(obj, CUSTOM_BPROP_NAME); //获取对象的反向传播函数
|
||||
|
||||
auto bprop_graph = std::make_shared<FuncGraph>();
|
||||
std::vector<AnfNodePtr> outputs;
|
||||
auto bprop_graph = std::make_shared<FuncGraph>(); //创建一个新的 FuncGraph 对象,用于构建反向传播图。
|
||||
std::vector<AnfNodePtr> outputs; //用于存储反向传播图的输出节点
|
||||
|
||||
auto fake_bprop = std::make_shared<PrimitivePy>("bprop_cut");
|
||||
auto fake_bprop = std::make_shared<PrimitivePy>("bprop_cut"); //创建一个新的 PrimitivePy 对象,用于构建反向传播图的输出节点。
|
||||
fake_bprop->AddBackwardHookFn(0, bprop_func);
|
||||
(void)fake_bprop->AddAttr(CUSTOM_BPROP_NAME, MakeValue(true));
|
||||
outputs.push_back(NewValueNode(fake_bprop));
|
||||
|
||||
py::object code_obj = py::getattr(bprop_func, "__code__");
|
||||
py::object code_obj = py::getattr(bprop_func, "__code__"); //获取反向传播函数的代码对象
|
||||
// Three parameters self, out and dout need to be excluded
|
||||
constexpr auto kBpropExcludeParamNum = 3;
|
||||
size_t inputs_num = py::cast<int64_t>(py::getattr(code_obj, "co_argcount")) - kBpropExcludeParamNum;
|
||||
for (size_t i = 0; i < inputs_num; ++i) {
|
||||
constexpr auto kBpropExcludeParamNum = 3; //反向传播函数的参数个数
|
||||
size_t inputs_num = py::cast<int64_t>(py::getattr(code_obj, "co_argcount")) - kBpropExcludeParamNum; //计算反向传播函数的输入参数个数
|
||||
for (size_t i = 0; i < inputs_num; ++i) { //创建反向传播函数的输入参数节点
|
||||
auto param = bprop_graph->add_parameter();
|
||||
outputs.push_back(param);
|
||||
}
|
||||
auto p1 = bprop_graph->add_parameter();
|
||||
auto p1 = bprop_graph->add_parameter(); //创建反向传播函数的输出参数节点
|
||||
auto p2 = bprop_graph->add_parameter();
|
||||
outputs.push_back(p1);
|
||||
outputs.push_back(p2);
|
||||
|
||||
bprop_graph->set_output(bprop_graph->NewCNode(std::move(outputs)));
|
||||
data_converter::SetObjGraphValue(obj_key, bprop_graph);
|
||||
bprop_graph->set_output(bprop_graph->NewCNode(std::move(outputs))); //设置反向传播函数的输出节点
|
||||
data_converter::SetObjGraphValue(obj_key, bprop_graph); //将反向传播函数的 FuncGraphPtr 对象存储到全局字典中
|
||||
return bprop_graph;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,31 +22,32 @@ from mindspore.ops import composite as C
|
|||
from mindspore.ops import functional as F
|
||||
from mindspore.ops import operations as P
|
||||
from mindspore.ops.operations._inner_ops import Send, Receive
|
||||
|
||||
|
||||
#from copy, hashlib, math and other other files export the required packages
|
||||
#The content of this document defines a distributed machine learning gradient fusion algorithm-Adasum,Adaptive Summary can improve the accuracy of training for clusters of different scales and reduce the difficulty of parameter tuning for clusters of different scales.
|
||||
__all__ = ["AdaSum"]
|
||||
|
||||
|
||||
MAX_NUM_HASH = 2 ** 31
|
||||
MAX_NUM_HASH = 2 ** 31 #Hash Table Parameters
|
||||
|
||||
|
||||
_update_parameters = C.MultitypeFuncGraph("update_parameters")
|
||||
_update_parameters = C.MultitypeFuncGraph("update_parameters") #Define update parameters and call the MultitypeFuncGraph class of composite in mindspore.ops. MultitypeFuncGraph is a class used to generate overloaded functions, using different types as input.
|
||||
#Decorate register types with a register register register with input types. Enable this function to use different types as input calls.
|
||||
|
||||
|
||||
@_update_parameters.register("Tensor", "Tensor", "Tensor", "Tensor")
|
||||
def _update_parameters_after_broadcast(delta_weight, update_delta_weight, parameter, old_parameter):
|
||||
shape = F.shape(delta_weight)
|
||||
update_delta_weight = P.Reshape()(update_delta_weight, shape)
|
||||
new_parameter = old_parameter - update_delta_weight
|
||||
return P.Assign()(parameter, new_parameter)
|
||||
@_update_parameters.register("Tensor", "Tensor", "Tensor", "Tensor") #The input types for register are four "Tensor".
|
||||
def _update_parameters_after_broadcast(delta_weight, update_delta_weight, parameter, old_parameter): #after broadcasting, update the parameters.
|
||||
shape = F.shape(delta_weight) #get the shape of functional
|
||||
update_delta_weight = P.Reshape()(update_delta_weight, shape) #using reshape operation to get the new delta_weight.
|
||||
new_parameter = old_parameter - update_delta_weight #get new parameters using sub subtraction.
|
||||
return P.Assign()(parameter, new_parameter) #return the tensor using the Assign which has two Variable.
|
||||
|
||||
|
||||
def _send_before_receive(send_part, send, recv):
|
||||
def _send_before_receive(send_part, send, recv): #using send to finish this work.
|
||||
send_ok = send(send_part)
|
||||
return recv(send_ok)
|
||||
|
||||
|
||||
def _receive_before_send(send_part, send, recv):
|
||||
def _receive_before_send(send_part, send, recv): #receive the send result.
|
||||
receive_ok = recv(send_part)
|
||||
send_part = F.depend(send_part, receive_ok)
|
||||
return F.depend(receive_ok, send(send_part))
|
||||
|
|
@ -156,7 +157,7 @@ class AdaSum(Cell):
|
|||
Outputs:
|
||||
- **adasum_parameters** (Tuple(Tensor)) - Tuple of parameters after adasum process.
|
||||
"""
|
||||
def __init__(self, rank, device_number, group_number, parameter_tuple):
|
||||
def __init__(self, rank, device_number, group_number, parameter_tuple): #Initialize the Adasum class and declare the hypermap and _geberate_communication_op.
|
||||
super(AdaSum, self).__init__()
|
||||
self.rank = rank
|
||||
self.device_number = device_number
|
||||
|
|
@ -165,7 +166,7 @@ class AdaSum(Cell):
|
|||
self._generate_communication_op()
|
||||
self.hyper_map = C.HyperMap()
|
||||
|
||||
def _generate_communication_op(self):
|
||||
def _generate_communication_op(self): #Define this method specifically.
|
||||
"""generate communication op."""
|
||||
self.calc_times = int(math.log(self.group_number, 2))
|
||||
self.send_node = []
|
||||
|
|
@ -178,28 +179,28 @@ class AdaSum(Cell):
|
|||
self.parameter_divisibility_list = []
|
||||
self.allreduce_node_num_list = []
|
||||
last_delta_weights = []
|
||||
group_start_rank = (self.rank // self.device_number) * self.device_number
|
||||
group_start_rank = (self.rank // self.device_number) * self.device_number #declare some list and some variable.
|
||||
|
||||
for step in range(self.calc_times):
|
||||
current_group = self.device_number * (2 ** step)
|
||||
current_group = self.device_number * (2 ** step) #current_group=device_number*pow(2,step)
|
||||
sr_target = self.rank
|
||||
if (sr_target // current_group) % 2 == 0:
|
||||
dest_target = sr_target + current_group
|
||||
if (sr_target // current_group) % 2 == 0: #if sr_target divided by current_group is an even number.
|
||||
dest_target = sr_target + current_group #dest_target is the result of adding the two variables.
|
||||
self.send_node.append(True)
|
||||
else:
|
||||
dest_target = sr_target - current_group
|
||||
else: #if it is odd number.
|
||||
dest_target = sr_target - current_group #subtraction
|
||||
self.send_node.append(False)
|
||||
|
||||
neighbor_ids = []
|
||||
group_name_last = 0
|
||||
for index in range(2 ** (step + 1)):
|
||||
node_rank = self.rank // self.device_number
|
||||
double_d = 2 ** (step + 1)
|
||||
node_rank = self.rank // self.device_number #get every node rank
|
||||
double_d = 2 ** (step + 1)
|
||||
neighbor_id = (node_rank // double_d * double_d + index) * self.device_number + \
|
||||
self.rank % self.device_number
|
||||
self.rank % self.device_number #get neighbor_id using the 2**(step+1) and self.devicenumber and self.
|
||||
neighbor_ids.append(neighbor_id)
|
||||
group_name_last += neighbor_id
|
||||
group_name = "adasum_" + str(step) + "_" + str(group_name_last)
|
||||
group_name = "adasum_" + str(step) + "_" + str(group_name_last) #Utilize newly acquired neighbors_update group information with ID.
|
||||
create_group(group_name, neighbor_ids)
|
||||
|
||||
send_left = []
|
||||
|
|
@ -208,13 +209,13 @@ class AdaSum(Cell):
|
|||
recv_right = []
|
||||
allreduce_node_num = ()
|
||||
left_delta_weights, right_delta_weights, delta_weights_divisibility = \
|
||||
self._get_delta_weights_info(last_delta_weights)
|
||||
self._get_delta_weights_info(last_delta_weights) #using the _get_delta_weights_info method to update the variabls.
|
||||
self.parameter_divisibility_list.append(delta_weights_divisibility)
|
||||
weights_index = 0
|
||||
fusion_id = (step + 1) * 3
|
||||
for shape, dtype in left_delta_weights:
|
||||
for shape, dtype in left_delta_weights: #using hash to get the tag
|
||||
send_tag = self._hash(step, sr_target, weights_index)
|
||||
send = Send(sr_tag=send_tag, dest_rank=dest_target, group="hccl_world_group")
|
||||
send = Send(sr_tag=send_tag, dest_rank=dest_target, group="hccl_world_group") #choose the type of group is "hccl_world_group"
|
||||
send.add_prim_attr("fusion", fusion_id)
|
||||
recv_tag = self._hash(step, dest_target, weights_index)
|
||||
recv = Receive(sr_tag=recv_tag, src_rank=dest_target, shape=shape, dtype=dtype,
|
||||
|
|
@ -269,38 +270,42 @@ class AdaSum(Cell):
|
|||
def _get_delta_weights_info(self, last_delta_weights):
|
||||
"""get delta weights info."""
|
||||
half_delta_weights = []
|
||||
if last_delta_weights:
|
||||
if last_delta_weights: #if the last one is exist, we should use the last one
|
||||
half_delta_weights = last_delta_weights
|
||||
else:
|
||||
for parameter in self.parameter_tuple:
|
||||
for parameter in self.parameter_tuple: # else we need to create a new half_delta_weights using the parameter
|
||||
new_shape = [int(x) for x in parameter.shape]
|
||||
half_delta_weights.append((new_shape, parameter.dtype))
|
||||
left_delta_weights = []
|
||||
right_delta_weights = []
|
||||
delta_weights_divisibility = ()
|
||||
for shape, dtype in half_delta_weights:
|
||||
for shape, dtype in half_delta_weights: #init
|
||||
left_shape = copy.deepcopy(shape)
|
||||
right_shape = copy.deepcopy(shape)
|
||||
divisibility_flag = False
|
||||
for i in range(len(shape)):
|
||||
for i in range(len(shape)): #left and right are half part of init shape
|
||||
if shape[i] > 1:
|
||||
left_shape[i] = int(shape[i] // 2)
|
||||
right_shape[i] = shape[i] - int(shape[i] // 2)
|
||||
divisibility_flag = True
|
||||
divisibility_flag = True #set the flag is true, which means exists the matrix which shape > 1
|
||||
break
|
||||
left_delta_weights.append((left_shape, dtype))
|
||||
right_delta_weights.append((right_shape, dtype))
|
||||
delta_weights_divisibility += (divisibility_flag,)
|
||||
delta_weights_divisibility += (divisibility_flag,) #count the num of shape > 1
|
||||
return left_delta_weights, right_delta_weights, delta_weights_divisibility
|
||||
|
||||
def _hash(self, step, target, weights_index):
|
||||
target = "tag" + str(step) + str(target) + str(weights_index)
|
||||
target = "tag" + str(step) + str(target) + str(weights_index) #combinate the step, target,a nd weights index as the target
|
||||
target_hash = hashlib.sha1(target.encode()).hexdigest()
|
||||
hash_res = int(int(target_hash, 16) % MAX_NUM_HASH)
|
||||
hash_res = int(int(target_hash, 16) % MAX_NUM_HASH) #ensure the result is "int" type
|
||||
return hash_res
|
||||
|
||||
def construct(self, delta_weights, parameters, old_parameters):
|
||||
forward_weights = [delta_weights]
|
||||
forward_weights = [delta_weights] #init the forward_weights
|
||||
"""HyperMap is a special class that requires passing in the mapping function f when constructing class objects, and passing in n parameter sequences of f when calling objects.
|
||||
For more usage methods, see HyperMap. The mapping function f must be of type MultitypeFuncGraph, which can be referenced.
|
||||
When using the for loop to batch process list elements, network compilation performance can be optimized through HyperMap equivalent semantic substitution.
|
||||
"""
|
||||
for i in range(self.calc_times):
|
||||
process_weights = self.hyper_map(F.partial(_adasum_opt_forward, self.send_node[i], self.allreduce_list[i]),
|
||||
self.parameter_divisibility_list[i], self.allreduce_node_num_list[i],
|
||||
|
|
@ -313,5 +318,5 @@ class AdaSum(Cell):
|
|||
self.send_list_rollback[j], self.recv_list_rollback[j])
|
||||
forward_weights[j] = process_weights
|
||||
adasum_parameters = self.hyper_map(F.partial(_update_parameters), delta_weights, forward_weights[0],
|
||||
parameters, old_parameters)
|
||||
parameters, old_parameters) #after the hyper_map optimize will get the adasum_paramater
|
||||
return adasum_parameters
|
||||
|
|
|
|||
|
|
@ -21,14 +21,14 @@ import numpy as np
|
|||
from scipy import linalg as la
|
||||
from mindspore.context import ParallelMode
|
||||
import mindspore.nn as nn
|
||||
from mindspore.nn.optim import LARS
|
||||
from mindspore.nn.optim import LARS #import the Layer-wise Adaptive Rate Scaling.
|
||||
from mindspore import log as logger
|
||||
from mindspore.common import Parameter
|
||||
from mindspore.common import Parameter #`Parameter` is a `Tensor` subclass, when they are assigned as Cell attributes they are automatically added to the list of it`s parameters.
|
||||
from mindspore.communication.management import get_group_size
|
||||
from mindspore.train.serialization import load_checkpoint
|
||||
from mindspore.parallel._utils import _get_global_rank
|
||||
from mindspore.parallel._auto_parallel_context import auto_parallel_context
|
||||
from .less_batch_normalization import CommonHeadLastFN
|
||||
from .less_batch_normalization import CommonHeadLastFN #from other documents import something.
|
||||
|
||||
|
||||
__all__ = ["OptimizerProcess", "ParameterProcess"]
|
||||
|
|
@ -67,7 +67,7 @@ class OptimizerProcess:
|
|||
>>> optimizer = optimizer_process.generate_new_optimizer()
|
||||
"""
|
||||
def __init__(self, opt):
|
||||
if isinstance(opt, LARS):
|
||||
if isinstance(opt, LARS): #Use isinstance to determine whether the optimizer is LARS,which is a Iterative algorithm for fast Feature selection and regression coefficient calculation for linear regression problems.
|
||||
self.is_lars = True
|
||||
self.single_opt = opt.opt
|
||||
self.opt_class = type(opt.opt)
|
||||
|
|
@ -80,7 +80,7 @@ class OptimizerProcess:
|
|||
self.opt_class = type(opt)
|
||||
self.opt_init_args = opt.init_args
|
||||
self.learning_rate = opt.init_learning_rate
|
||||
self.origin_params = opt.init_params["params"]
|
||||
self.origin_params = opt.init_params["params"] #set the origin_params as opt.init_params
|
||||
|
||||
def build_params_dict(self, network):
|
||||
r"""
|
||||
|
|
@ -89,9 +89,9 @@ class OptimizerProcess:
|
|||
Args:
|
||||
network (Cell): The training network.
|
||||
"""
|
||||
cells = network.cells_and_names()
|
||||
cells = network.cells_and_names() #Initialize cells.
|
||||
params_dict = {}
|
||||
for _, cell in cells:
|
||||
for _, cell in cells: #Traverse each cell and utilize cell.get_The comparison of parameters to parameters_Assignment operation of dict.
|
||||
for par in cell.get_parameters(expand=False):
|
||||
params_dict[id(par)] = cell
|
||||
return params_dict
|
||||
|
|
@ -105,31 +105,31 @@ class OptimizerProcess:
|
|||
parameters (list): The network's parameter list.
|
||||
"""
|
||||
group_params = []
|
||||
for group_param in parameters:
|
||||
if 'order_params' in group_param.keys():
|
||||
group_params.append(group_param)
|
||||
for group_param in parameters: #Fill in group_params.
|
||||
if 'order_params' in group_param.keys():
|
||||
group_params.append(group_param) #need to determine whether thers is an ¡®order_params' in the keys of group_param, only append when existing
|
||||
continue
|
||||
params_gc_value = []
|
||||
params_value = []
|
||||
for param in group_param['params']:
|
||||
if 'beta' not in param.name and 'gamma' not in param.name and 'bias' not in param.name:
|
||||
param_cell = params_dict[id(param)]
|
||||
if 'beta' not in param.name and 'gamma' not in param.name and 'bias' not in param.name: #param.name is not the ¡®beta' or the ¡®gamma' or the ¡®bias¡¯
|
||||
param_cell = params_dict[id(param)] #only in this situation ,the param_cell will be assigned.
|
||||
if (isinstance(param_cell, nn.Conv2d) and param_cell.group > 1) or \
|
||||
isinstance(param_cell, CommonHeadLastFN):
|
||||
params_value.append(param)
|
||||
params_value.append(param) #if the param_cell is nn.Conv2d and group > 1 or param_cell is CommonHeadLastFN.
|
||||
else:
|
||||
params_gc_value.append(param)
|
||||
params_gc_value.append(param) #or append into gc
|
||||
else:
|
||||
params_value.append(param)
|
||||
if params_gc_value:
|
||||
params_value.append(param) #if not, append into the params_value.
|
||||
if params_gc_value: #if list of gc is not null.
|
||||
new_group_param = copy.deepcopy(group_param)
|
||||
new_group_param['params'] = params_gc_value
|
||||
new_group_param['grad_centralization'] = True
|
||||
group_params.append(new_group_param)
|
||||
group_params.append(new_group_param) #append the params_gc_value to the group_params
|
||||
if params_value:
|
||||
new_group_param = copy.deepcopy(group_param)
|
||||
new_group_param['params'] = params_value
|
||||
group_params.append(new_group_param)
|
||||
group_params.append(new_group_param) #in the same way append the params_value to the group_params.
|
||||
return group_params
|
||||
|
||||
def add_grad_centralization(self, network):
|
||||
|
|
@ -141,27 +141,27 @@ class OptimizerProcess:
|
|||
"""
|
||||
params_dict = self.build_params_dict(network)
|
||||
|
||||
parameters = self.origin_params
|
||||
parameters = self.origin_params #set the orign parameters
|
||||
if parameters is not None and not isinstance(parameters, list):
|
||||
parameters = list(parameters)
|
||||
parameters = list(parameters) #convert the parameters to the lists
|
||||
|
||||
if not parameters:
|
||||
raise ValueError("Optimizer got an empty parameter list.")
|
||||
|
||||
if not isinstance(parameters[0], (dict, Parameter)):
|
||||
if not isinstance(parameters[0], (dict, Parameter)): #Illegal situation judgment.
|
||||
raise TypeError("Only a list of Parameter or dict can be supported.")
|
||||
|
||||
if isinstance(parameters[0], Parameter):
|
||||
logger.warning("Only group parameters support gradient centralization.")
|
||||
if isinstance(parameters[0], Parameter): #if the parameter is just the list of Parameter, the network can not solve it.
|
||||
logger.warning("Only group parameters support gradient centralization.") #so that means a warning need to be showed.
|
||||
return
|
||||
|
||||
self.origin_params = self.build_gc_params_group(params_dict, parameters)
|
||||
|
||||
def generate_new_optimizer(self):
|
||||
"""Generate new optimizer."""
|
||||
if self.learning_rate is None:
|
||||
if self.learning_rate is None: #check the learning_rate
|
||||
self.learning_rate = self.single_opt.learning_rate
|
||||
if not self.is_lars:
|
||||
if not self.is_lars: #check whether using the Least Angle Regression
|
||||
opt = self.opt_class(params=self.origin_params, learning_rate=self.learning_rate, **self.opt_init_args)
|
||||
else:
|
||||
opt = LARS(self.opt_class(params=self.origin_params, learning_rate=self.learning_rate, \
|
||||
|
|
@ -212,18 +212,18 @@ class ParameterProcess:
|
|||
parameters (list): The network's parameter list.
|
||||
split_point (list): The gradient split point of this network. default: None.
|
||||
"""
|
||||
if not isinstance(parameters, (list, tuple)) or not parameters:
|
||||
if not isinstance(parameters, (list, tuple)) or not parameters: #check whether the parameter's typt is list or tuple.
|
||||
return parameters
|
||||
|
||||
parameter_len = len(parameters)
|
||||
if split_point:
|
||||
if split_point: #using the split_point to assign the index
|
||||
split_parameter_index = split_point
|
||||
else:
|
||||
split_parameter_index = [parameter_len // 2]
|
||||
split_parameter_index = [parameter_len // 2] #or using the len
|
||||
for i in range(parameter_len):
|
||||
if i in split_parameter_index:
|
||||
self._parameter_indices += 1
|
||||
parameters[i].comm_fusion = self._parameter_indices
|
||||
parameters[i].comm_fusion = self._parameter_indices #assign the parameters[i].comm_fusion using the parametes_indices which is added by the index
|
||||
return parameters
|
||||
|
||||
def generate_group_params(self, parameters, origin_params):
|
||||
|
|
@ -234,10 +234,10 @@ class ParameterProcess:
|
|||
parameters (list): The network's parameter list.
|
||||
origin_params (list): The network's origin parameter list.
|
||||
"""
|
||||
origin_params_copy = origin_params
|
||||
origin_params_copy = origin_params #create a copy of the origin_params, which means we can revise the copy without changing the original one
|
||||
if origin_params_copy is not None:
|
||||
if not isinstance(origin_params_copy, list):
|
||||
origin_params_copy = list(origin_params_copy)
|
||||
origin_params_copy = list(origin_params_copy) #convert its type to list
|
||||
|
||||
if not origin_params_copy:
|
||||
raise ValueError("Optimizer got an empty parameter list.")
|
||||
|
|
@ -245,13 +245,13 @@ class ParameterProcess:
|
|||
if not isinstance(origin_params_copy[0], (dict, Parameter)):
|
||||
raise TypeError("Only a list of Parameter or dict can be supported.")
|
||||
|
||||
if isinstance(origin_params_copy[0], Parameter):
|
||||
if isinstance(origin_params_copy[0], Parameter): #some check about its legality.
|
||||
group_params = [{"params": parameters}]
|
||||
return group_params
|
||||
|
||||
# the operation below is similar to the operation in the build_gc_params_group,check the ¡®order_params' and fufill the group_params
|
||||
group_params = []
|
||||
params_name = [param.name for param in parameters]
|
||||
new_params_count = copy.deepcopy(params_name)
|
||||
new_params_count = copy.deepcopy(params_name) #using the deepcopy to get the new_params_count so we will not change the value of the original params_name
|
||||
new_params_clone = {}
|
||||
max_key_number = 0
|
||||
for group_param in origin_params_copy:
|
||||
|
|
@ -298,34 +298,34 @@ def _get_local_pca_mat_path(weight_load_dir, pca_mat_path, n_component, device_n
|
|||
"""
|
||||
if pca_mat_path is not None and os.path.exists(pca_mat_path) and os.path.isfile(pca_mat_path) and \
|
||||
pca_mat_path.endswith(".npy"):
|
||||
full_pca_mat_path = pca_mat_path
|
||||
pca_mat_exist = True
|
||||
full_pca_mat_path = pca_mat_path # if the pc_mat_path is exists in the os.path and isfile is OK,set the full_pca_mat_path and pca_mat_exists
|
||||
pca_mat_exist = True
|
||||
|
||||
else:
|
||||
if weight_load_dir is None or not os.path.exists(weight_load_dir) or not os.path.isdir(weight_load_dir):
|
||||
raise ValueError("The weight_load_dir: {} is None / not exists / not directory.".format(weight_load_dir))
|
||||
raise ValueError("The weight_load_dir: {} is None / not exists / not directory.".format(weight_load_dir)) #if not ,show the ValueError about "The weight_load_dir: {} is None / not exists / not directory".
|
||||
|
||||
full_pca_mat_path = os.path.join(weight_load_dir, "pca_mat_temp.npy")
|
||||
pca_mat_exist = False
|
||||
|
||||
save_pca_end_path = os.path.join(os.path.dirname(full_pca_mat_path), "save_pca_end.txt")
|
||||
if os.path.exists(save_pca_end_path):
|
||||
if os.path.exists(save_pca_end_path): #erase the old path.
|
||||
os.remove(save_pca_end_path)
|
||||
|
||||
rank = _get_global_rank()
|
||||
rank = _get_global_rank() #assign the rank using the _get_global_rank, so the rank is global
|
||||
local_pca_mat_path = full_pca_mat_path[:-4] + "_rank_" + str(rank) + ".npy"
|
||||
if os.path.exists(local_pca_mat_path):
|
||||
if os.path.exists(local_pca_mat_path): #erase the old path
|
||||
os.remove(local_pca_mat_path)
|
||||
if rank % device_number != 0:
|
||||
if rank % device_number != 0: #need to ensure the rank is the multiple of device_number
|
||||
return local_pca_mat_path
|
||||
|
||||
if pca_mat_exist:
|
||||
pca_mat = np.load(full_pca_mat_path)
|
||||
pca_mat = np.load(full_pca_mat_path) #using numpy to get the pac_mat
|
||||
else:
|
||||
data = _load_weights(weight_load_dir, network)
|
||||
data = _load_weights(weight_load_dir, network) #assign data using the _load_weights function which args is where to load the weighs and the network the weights ues to defined below.
|
||||
pca_mat = _compute_pca_mat(data, n_component)
|
||||
np.save(full_pca_mat_path, pca_mat)
|
||||
_save_local_pca_mat(pca_mat, full_pca_mat_path, n_component)
|
||||
_save_local_pca_mat(pca_mat, full_pca_mat_path, n_component) #the save function is completed by the function named _save_local_pca_mat
|
||||
return local_pca_mat_path
|
||||
|
||||
|
||||
|
|
@ -338,7 +338,7 @@ def _load_weights(weight_load_dir, network):
|
|||
network (Cell): The network.
|
||||
"""
|
||||
param_requires_grad_list = []
|
||||
for param in network.trainable_params():
|
||||
for param in network.trainable_params(): #fufill the param_list using the param.name
|
||||
param_requires_grad_list.append(param.name)
|
||||
|
||||
param_mat_tuple = ()
|
||||
|
|
@ -346,19 +346,19 @@ def _load_weights(weight_load_dir, network):
|
|||
for file in weight_file_list:
|
||||
if not file.endswith('.ckpt'):
|
||||
continue
|
||||
file_path = os.path.join(weight_load_dir, file)
|
||||
param_dict = load_checkpoint(file_path)
|
||||
file_path = os.path.join(weight_load_dir, file) #get the file path using the dir and file name
|
||||
param_dict = load_checkpoint(file_path) #we can use the file_path to set a checkpoint to save the weights
|
||||
param_tuple = ()
|
||||
for key, value in param_dict.items():
|
||||
if key in param_requires_grad_list:
|
||||
if key in param_requires_grad_list: #check whether the key is in the param_requires_grad_list to operate the param_tuple
|
||||
param_tuple += (value.asnumpy().reshape((1, -1)),)
|
||||
param = np.concatenate(param_tuple, axis=1)
|
||||
param = np.concatenate(param_tuple, axis=1) #concatenate all the tuple to the param
|
||||
param_mat_tuple += (param,)
|
||||
param_mat = np.concatenate(param_mat_tuple, axis=0)
|
||||
param_mat = np.concatenate(param_mat_tuple, axis=0) #concatenate all the mat tuple to the param_mat
|
||||
return param_mat
|
||||
|
||||
|
||||
def _compute_pca_mat(data, n_component, randomized=True):
|
||||
def _compute_pca_mat(data, n_component, randomized=True): #randomized=True means the components is the result of the _randomized_svd.
|
||||
"""
|
||||
compute pca mat.
|
||||
|
||||
|
|
@ -375,7 +375,7 @@ def _compute_pca_mat(data, n_component, randomized=True):
|
|||
if randomized:
|
||||
components = _randomized_svd(data, n_component)
|
||||
else:
|
||||
components = _full_svd(data, n_component)
|
||||
components = _full_svd(data, n_component) #or no need to randomized it
|
||||
|
||||
return components
|
||||
|
||||
|
|
@ -392,24 +392,24 @@ def _randomized_svd(data, n_component, n_oversample=10, n_iter=1):
|
|||
n_oversample (int): oversample num
|
||||
n_iter (int): iteration count
|
||||
"""
|
||||
mean = np.mean(data, axis=0)
|
||||
mean = np.mean(data, axis=0) #get the average
|
||||
data -= mean
|
||||
n_random = n_component + n_oversample
|
||||
n_random = n_component + n_oversample #the n_random is the addition of the n_component and the n_oversample
|
||||
n_samples, n_features = data.shape
|
||||
transpose = n_samples < n_features
|
||||
transpose = n_samples < n_features # the value transpose recorsd the relation between the n_samples and the n_features
|
||||
if transpose:
|
||||
data = data.T
|
||||
data = data.T #Determine whether the data matrix needs to be transposed based on samples and features
|
||||
q_mat = _randomized_range_finder(data, n_random, n_iter)
|
||||
b_mat = q_mat.T @ data
|
||||
u_hat, _, vt_mat = la.svd(b_mat, full_matrices=False)
|
||||
b_mat = q_mat.T @ data #b_mat is the randomed result and the multiply of data
|
||||
u_hat, _, vt_mat = la.svd(b_mat, full_matrices=False) #using the la.svd
|
||||
del b_mat
|
||||
u_mat = np.dot(q_mat, u_hat)
|
||||
u_mat, vt_mat = _svd_flip(u_mat, vt_mat, transpose)
|
||||
u_mat, vt_mat = _svd_flip(u_mat, vt_mat, transpose) #using the _svd_flip
|
||||
if transpose:
|
||||
components = u_mat[:, :n_component].T
|
||||
else:
|
||||
components = vt_mat[:n_component, :]
|
||||
return components
|
||||
return components #components depends on the number of samples and features,if the ranspose is true, usinf the u_ma, or the vt_mat.
|
||||
|
||||
|
||||
def _full_svd(data, n_component):
|
||||
|
|
@ -422,11 +422,11 @@ def _full_svd(data, n_component):
|
|||
and `n_features` is the number of features.
|
||||
n_component (int): pca component.
|
||||
"""
|
||||
mean = np.mean(data, axis=0)
|
||||
mean = np.mean(data, axis=0) #get the average
|
||||
data -= mean
|
||||
u, _, v = la.svd(data, full_matrices=False)
|
||||
_, v = _svd_flip(u, v)
|
||||
components = v[:n_component]
|
||||
components = v[:n_component] #from 0 to n_component
|
||||
return components
|
||||
|
||||
|
||||
|
|
@ -444,7 +444,7 @@ def _randomized_range_finder(data, size, n_iter=1):
|
|||
q_mat = np.random.normal(size=(data.shape[1], size))
|
||||
|
||||
for _ in range(n_iter):
|
||||
q_mat, _ = la.lu(data @ q_mat, permute_l=True)
|
||||
q_mat, _ = la.lu(data @ q_mat, permute_l=True)
|
||||
q_mat, _ = la.lu(data.T @ q_mat, permute_l=True)
|
||||
|
||||
q_mat, _ = la.qr(data @ q_mat, mode="economic")
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from mindspore import log as logger
|
|||
|
||||
|
||||
def preprocess_imagenet_validation_dataset(train_dataset_path, validation_dataset_path, image_label_mapping_file):
|
||||
# this function is used to preprocess imagenet validation dataset
|
||||
"""
|
||||
Call this function before read imagenet validation dataset.
|
||||
|
||||
|
|
@ -30,19 +31,22 @@ def preprocess_imagenet_validation_dataset(train_dataset_path, validation_datase
|
|||
"""
|
||||
train_dataset_path = os.path.realpath(train_dataset_path)
|
||||
sub_dir = [dir_.name for dir_ in os.scandir(train_dataset_path) if dir_.is_dir()]
|
||||
# create sub dir
|
||||
for sub_dir_name in sub_dir:
|
||||
validate_sub_dir = os.path.join(validation_dataset_path, sub_dir_name)
|
||||
validate_sub_dir = os.path.realpath(validate_sub_dir)
|
||||
if not os.path.exists(validate_sub_dir):
|
||||
os.makedirs(validate_sub_dir, mode=stat.S_IRWXU)
|
||||
real_file_path = os.path.realpath(image_label_mapping_file)
|
||||
real_file_path = os.path.realpath(image_label_mapping_file) # imagenet_validate_dataset_2012_image_dir_map.txt
|
||||
mappings = [mapping.strip() for mapping in open(real_file_path).readlines()]
|
||||
# move image to sub dir
|
||||
for mapping in mappings:
|
||||
image_dir = mapping.split(':')
|
||||
old_image_path = os.path.join(validation_dataset_path, image_dir[0])
|
||||
old_image_path = os.path.realpath(old_image_path)
|
||||
if not os.path.exists(old_image_path):
|
||||
if not os.path.exists(old_image_path): # imagenet_validate_dataset_2012_image_dir_map.txt
|
||||
logger.warning('Image is not existed %s', old_image_path)
|
||||
# move image to sub dir
|
||||
new_image_sub_dir = os.path.join(validation_dataset_path, image_dir[1])
|
||||
new_image_sub_dir = os.path.realpath(new_image_sub_dir)
|
||||
new_image_path = os.path.join(new_image_sub_dir, image_dir[0])
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ def imshow_det_bbox(image, bboxes, labels, segm=None, class_names=None, score_th
|
|||
image = image.transpose((1, 2, 0))
|
||||
draw_image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
|
||||
|
||||
if bboxes is not None:
|
||||
if bboxes is not None: # draw bboxes
|
||||
bbox_num = bboxes.shape[0]
|
||||
for i in range(bbox_num):
|
||||
draw_bbox = bboxes[i]
|
||||
|
|
@ -165,7 +165,7 @@ def imshow_det_bbox(image, bboxes, labels, segm=None, class_names=None, score_th
|
|||
if segm is not None:
|
||||
mask = segm[i].astype(bool)
|
||||
draw_image[mask] = draw_image[mask] * 0.5 + np.array(mask_color) * 0.5
|
||||
else:
|
||||
else: # draw segm
|
||||
if segm is not None:
|
||||
segm_num = segm.shape[0]
|
||||
for i in range(segm_num):
|
||||
|
|
|
|||
|
|
@ -43,13 +43,13 @@ from .dtypes import nan, pi
|
|||
|
||||
# According to official numpy reference, the dimension of a numpy array must be less
|
||||
# than 32
|
||||
MAX_NUMPY_DIMS = 32
|
||||
MAX_NUMPY_DIMS = 32 #this is a restriction of the matrix division
|
||||
# All types that can be accepted as "array_like" parameters in graph mode.
|
||||
ARRAY_TYPES = (int, float, bool, list, tuple, Tensor)
|
||||
|
||||
_reduce_min_keepdims = P.ReduceMin(True)
|
||||
_reduce_max_keepdims = P.ReduceMax(True)
|
||||
_reduce_mean_keepdims = P.ReduceMean(True)
|
||||
_reduce_min_keepdims = P.ReduceMin(True) #initialize the keepdims
|
||||
_reduce_max_keepdims = P.ReduceMax(True)
|
||||
_reduce_mean_keepdims = P.ReduceMean(True)
|
||||
|
||||
|
||||
def array(obj, dtype=None, copy=True, ndmin=0):
|
||||
|
|
@ -89,7 +89,7 @@ def array(obj, dtype=None, copy=True, ndmin=0):
|
|||
dtype = _check_dtype(dtype)
|
||||
res = asarray(obj, dtype)
|
||||
|
||||
if ndmin > res.ndim:
|
||||
if ndmin > res.ndim: #check whether the dim go beyond the limitation
|
||||
if res.size == 0:
|
||||
_raise_value_error("Empty tensor cannot be expanded beyond the current dimension.")
|
||||
res = _expand(res, ndmin)
|
||||
|
|
@ -110,10 +110,10 @@ def asarray_const(a, dtype=None):
|
|||
if dtype is not None:
|
||||
dtype = _check_dtype(dtype)
|
||||
|
||||
if isinstance(a, (float, int, bool)) and dtype is None:
|
||||
if isinstance(a, (float, int, bool)) and dtype is None: #float, int, bool meams the single constant.
|
||||
dtype = _get_dtype_from_scalar(a)
|
||||
|
||||
if isinstance(a, (list, tuple)):
|
||||
if isinstance(a, (list, tuple)): #or if the type is list or tuple, which is an ITERATOR
|
||||
# Convert all tuple/nested tuples to lists
|
||||
a = _deep_list(a)
|
||||
# Convert all tensor sub-elements to numpy arrays
|
||||
|
|
@ -167,17 +167,17 @@ def asarray(a, dtype=None):
|
|||
>>> print(np.asarray([1,2,3]))
|
||||
[1 2 3]
|
||||
"""
|
||||
if dtype is not None:
|
||||
if dtype is not None: #before the operation, need to check the dtype to ensure its legality.
|
||||
dtype = _check_dtype(dtype)
|
||||
if isinstance(a, Tensor):
|
||||
if dtype is None or dtype == a.dtype:
|
||||
return a
|
||||
if dtype is None or dtype == a.dtype: #if a is a Tensor and its dtype is the dtype, the function will return a
|
||||
return a #else return a.astype
|
||||
return a.astype(dtype)
|
||||
return asarray_const(a, dtype)
|
||||
return asarray_const(a, dtype) #if a is not a tensor, then a is a const, return the result of asarray_const
|
||||
|
||||
|
||||
@constexpr
|
||||
def asfarray_const(a, dtype=mstype.float32):
|
||||
def asfarray_const(a, dtype=mstype.float32): # the function is similar to the
|
||||
"""Converts the input to tensor. Note here `a` cannot be tensor itself."""
|
||||
_check_input_for_asarray(a)
|
||||
if isinstance(a, (list, tuple)):
|
||||
|
|
@ -185,7 +185,7 @@ def asfarray_const(a, dtype=mstype.float32):
|
|||
a = _deep_list(a)
|
||||
# Convert all tensor sub-elements to numpy arrays
|
||||
a = _deep_tensor_to_nparray(a)
|
||||
a = onp.asarray(a)
|
||||
a = onp.asarray(a)
|
||||
if a.dtype is onp.dtype('object'):
|
||||
raise ValueError(f"For Tensor conversion, the input_data is {a} that contains unsupported element.")
|
||||
a = Tensor.from_numpy(a)
|
||||
|
|
@ -225,8 +225,8 @@ def asfarray(a, dtype=mstype.float32):
|
|||
if dtype is None:
|
||||
return asarray(a)
|
||||
|
||||
dtype = _check_dtype(dtype)
|
||||
if dtype not in (mstype.float16, mstype.float32, mstype.float64):
|
||||
dtype = _check_dtype(dtype)
|
||||
if dtype not in (mstype.float16, mstype.float32, mstype.float64): #three type of the dtype , which means the numpy have three type of float ,16,32 and 64.
|
||||
dtype = mstype.float32
|
||||
|
||||
if isinstance(a, Tensor):
|
||||
|
|
@ -260,7 +260,7 @@ def copy_(a):
|
|||
[[1. 1.]
|
||||
[1. 1.]]
|
||||
"""
|
||||
a = asarray(a)
|
||||
a = asarray(a) #ensure the a is an array
|
||||
return a.copy()
|
||||
|
||||
|
||||
|
|
@ -289,11 +289,11 @@ def ones(shape, dtype=mstype.float32):
|
|||
[[1. 1.]
|
||||
[1. 1.]]
|
||||
"""
|
||||
shape = _check_shape(shape)
|
||||
dtype = _check_dtype(dtype)
|
||||
if _is_shape_empty(shape):
|
||||
shape = _check_shape(shape)
|
||||
dtype = _check_dtype(dtype) #check the shape and dtype
|
||||
if _is_shape_empty(shape): #if the shape is null, fufill the array with the type of dtype and the shape of it, create the array full of "1.0"
|
||||
return full(shape, 1.0, dtype)
|
||||
output = F.fill(dtype, shape, 1)
|
||||
output = F.fill(dtype, shape, 1) #or using the functional->fill get a tensor full of "1"
|
||||
return output
|
||||
|
||||
|
||||
|
|
@ -325,8 +325,8 @@ def zeros(shape, dtype=mstype.float32):
|
|||
shape = _check_shape(shape)
|
||||
dtype = _check_dtype(dtype)
|
||||
if _is_shape_empty(shape):
|
||||
return full(shape, 0.0, dtype)
|
||||
output = F.fill(dtype, shape, 0)
|
||||
return full(shape, 0.0, dtype) #the type of the tensor is float 32.
|
||||
output = F.fill(dtype, shape, 0) #but the output type is the integer
|
||||
return output
|
||||
|
||||
|
||||
|
|
@ -360,7 +360,7 @@ def full(shape, fill_value, dtype=None):
|
|||
[True True]]
|
||||
"""
|
||||
shape = _check_shape(shape)
|
||||
if not isinstance(fill_value, ARRAY_TYPES):
|
||||
if not isinstance(fill_value, ARRAY_TYPES): #check the fill_value, need ensure the
|
||||
_raise_type_error("fill value should be int, float, bool, list, tuple, Tensor, but got", fill_value)
|
||||
if dtype is not None:
|
||||
dtype = _check_dtype(dtype)
|
||||
|
|
@ -376,7 +376,7 @@ def full(shape, fill_value, dtype=None):
|
|||
if isinstance(fill_value, (list, tuple)):
|
||||
fill_value = asarray_const(fill_value)
|
||||
return broadcast_to(fill_value, shape)
|
||||
# if shape contains zero, use c.Tensor()
|
||||
# if shape contains zero, use c.Tensor() #using the _convert function to get the 32 type
|
||||
return _convert_64_to_32(empty_compile(dtype, shape))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -99,13 +99,13 @@ def absolute(x, dtype=None):
|
|||
"""
|
||||
original_dtype = x.dtype
|
||||
allowed_types = None
|
||||
if _get_device() == "Ascend":
|
||||
allowed_types = (mstype.float16, mstype.float32)
|
||||
if _get_device() == "Ascend": #if the device is Ascend
|
||||
allowed_types = (mstype.float16, mstype.float32) # it can support the service of float64 and float32, can not support int32 type
|
||||
else:
|
||||
allowed_types = (mstype.int32, mstype.float16, mstype.float32, mstype.float64)
|
||||
if original_dtype not in allowed_types and dtype is None:
|
||||
x = x.astype(mstype.float32)
|
||||
return _apply_tensor_op(F.absolute, x, dtype=dtype).astype(original_dtype)
|
||||
x = x.astype(mstype.float32) #Default is float32
|
||||
return _apply_tensor_op(F.absolute, x, dtype=dtype).astype(original_dtype) #return the result of astype function, which is the result after absolute operation
|
||||
return _apply_tensor_op(F.absolute, x, dtype=dtype)
|
||||
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ def count_nonzero(x, axis=None, keepdims=False):
|
|||
>>> print(output)
|
||||
6
|
||||
"""
|
||||
if _is_shape_empty(x.shape):
|
||||
if _is_shape_empty(x.shape): #check the x.shape
|
||||
return ZERO_TENSOR
|
||||
if axis is None:
|
||||
axis = ()
|
||||
|
|
@ -184,9 +184,9 @@ def clip(x, xmin, xmax, dtype=None):
|
|||
>>> print(output)
|
||||
[1 2 2 0 0 2 2 0]
|
||||
"""
|
||||
if xmin is None and xmax is None:
|
||||
if xmin is None and xmax is None: #at least having one limitation
|
||||
_raise_value_error("One of max or min must be given.")
|
||||
if xmin is not None:
|
||||
if xmin is not None: #using the max or min function to get the lmitation
|
||||
x = maximum(x, xmin, dtype=dtype)
|
||||
if xmax is not None:
|
||||
x = minimum(x, xmax, dtype=dtype)
|
||||
|
|
@ -222,7 +222,7 @@ def deg2rad(x, dtype=None):
|
|||
_check_input_tensor(x)
|
||||
|
||||
def convert(a):
|
||||
return a * pi / 180.0
|
||||
return a * pi / 180.0 #the type is float
|
||||
return _apply_tensor_op(convert, x, dtype=dtype)
|
||||
|
||||
|
||||
|
|
@ -252,7 +252,7 @@ def rad2deg(x, dtype=None):
|
|||
_check_input_tensor(x)
|
||||
|
||||
def convert(a):
|
||||
return a * 180.0 / pi
|
||||
return a * 180.0 / pi #the type is float
|
||||
return _apply_tensor_op(convert, x, dtype=dtype)
|
||||
|
||||
|
||||
|
|
@ -289,7 +289,7 @@ def add(x1, x2, dtype=None):
|
|||
"""
|
||||
# broadcast is not fully supported in tensor_add on CPU,
|
||||
# so we use tensor_sub as a substitute solution
|
||||
if _get_device() == 'CPU':
|
||||
if _get_device() == 'CPU': #ADD function call the substract if the device is CPU
|
||||
return subtract(x1, F.neg_tensor(_to_tensor(x2)), dtype=dtype)
|
||||
return _apply_tensor_op(F.tensor_add, x1, x2, dtype=dtype)
|
||||
|
||||
|
|
@ -359,12 +359,12 @@ def multiply(x1, x2, dtype=None):
|
|||
[3 8]
|
||||
[3 8]]
|
||||
"""
|
||||
if _get_device() == 'CPU':
|
||||
if _get_device() == 'CPU': #if the device is CPU, need to check the tensor
|
||||
_check_input_tensor(x1, x2)
|
||||
# broadcast is not fully supported on CPU backend,
|
||||
# and explicit broadcasting is performed
|
||||
shape_out = _infer_out_shape(F.shape(x1), F.shape(x2))
|
||||
x1 = _broadcast_to_shape(x1, shape_out)
|
||||
shape_out = _infer_out_shape(F.shape(x1), F.shape(x2)) #show the parameter of the shape
|
||||
x1 = _broadcast_to_shape(x1, shape_out) #broadcasting the x1,x2
|
||||
x2 = _broadcast_to_shape(x2, shape_out)
|
||||
return _apply_tensor_op(F.tensor_mul, x1, x2, dtype=dtype)
|
||||
|
||||
|
|
@ -404,7 +404,7 @@ def divide(x1, x2, dtype=None):
|
|||
"""
|
||||
x1, x2 = _to_tensor(x1, x2)
|
||||
if not _check_is_float(F.dtype(x1)) and not _check_is_float(F.dtype(x2)):
|
||||
x1 = F.cast(x1, mstype.float32)
|
||||
x1 = F.cast(x1, mstype.float32) #need to cast it to the float32
|
||||
x2 = F.cast(x2, mstype.float32)
|
||||
return _apply_tensor_op(F.tensor_div, x1, x2, dtype=dtype)
|
||||
|
||||
|
|
@ -442,7 +442,7 @@ def true_divide(x1, x2, dtype=None):
|
|||
[0.33333334 0.5 ]
|
||||
[0.33333334 0.5 ]]
|
||||
"""
|
||||
return divide(x1, x2, dtype=dtype)
|
||||
return divide(x1, x2, dtype=dtype) #noe need to do that because its integer division
|
||||
|
||||
|
||||
def power(x1, x2, dtype=None):
|
||||
|
|
@ -522,7 +522,7 @@ def float_power(x1, x2, dtype=None):
|
|||
if not _check_same_type(F.dtype(x1), mstype.float32):
|
||||
x1 = F.cast(x1, mstype.float32)
|
||||
if not _check_same_type(F.dtype(x2), mstype.float32):
|
||||
x2 = F.cast(x2, mstype.float32)
|
||||
x2 = F.cast(x2, mstype.float32) #convert the value to the float32
|
||||
|
||||
return _apply_tensor_op(F.tensor_pow, x1, x2, dtype=dtype)
|
||||
|
||||
|
|
@ -562,7 +562,7 @@ def minimum(x1, x2, dtype=None):
|
|||
[[1 2]
|
||||
[1 2]]
|
||||
"""
|
||||
if isinstance(x1, (int, float, bool, list, tuple)):
|
||||
if isinstance(x1, (int, float, bool, list, tuple)):
|
||||
x1 = asarray_const(x1)
|
||||
elif not isinstance(x1, Tensor):
|
||||
_raise_type_error("Input x1 is expected to be array_like")
|
||||
|
|
@ -676,15 +676,15 @@ def inner(a, b):
|
|||
[[3. 3. 3. 3. 3. 3. 3.]
|
||||
[3. 3. 3. 3. 3. 3. 3.]]]
|
||||
"""
|
||||
if F.rank(a) == 0 or F.rank(b) == 0:
|
||||
if F.rank(a) == 0 or F.rank(b) == 0: #if the rank of a or the rank of b is zero, then the inner of them is the result of multiply,For one-dimensional tensors, they are all the same
|
||||
return F.tensor_mul(a, b)
|
||||
|
||||
_check_shape_aligned(F.shape(a), F.shape(b))
|
||||
aligned_shape_a = (F.shape_mul(F.shape(a)[:-1]), F.shape(a)[-1])
|
||||
aligned_shape_a = (F.shape_mul(F.shape(a)[:-1]), F.shape(a)[-1]) #using the shape of a[:-1] and the last rank of a to get the aligened_shape
|
||||
aligned_shape_b = (F.shape_mul(F.shape(b)[:-1]), F.shape(a)[-1])
|
||||
a_aligned = F.reshape(a, aligned_shape_a)
|
||||
a_aligned = F.reshape(a, aligned_shape_a) #using the aligened a to reshape the a
|
||||
b_aligned = F.reshape(b, aligned_shape_b)
|
||||
|
||||
#the qperation upon is to reshape the a and b to adjust to the _mat_mul_t function.
|
||||
res = _matmul_t(a_aligned, b_aligned)
|
||||
res = F.reshape(res, F.shape(a)[:-1] + F.shape(b)[:-1])
|
||||
return res
|
||||
|
|
@ -736,21 +736,21 @@ def dot(a, b):
|
|||
[[[105. 105. 105. 105.]
|
||||
[105. 105. 105. 105.]]]
|
||||
"""
|
||||
ndim_a, ndim_b = F.rank(a), F.rank(b)
|
||||
ndim_a, ndim_b = F.rank(a), F.rank(b) #get the rank of a and b
|
||||
if ndim_a == 0 or ndim_b == 0:
|
||||
return F.tensor_mul(a, b)
|
||||
if ndim_a > 0 and ndim_b >= 2:
|
||||
perm = F.make_range(ndim_b)
|
||||
return F.tensor_mul(a, b) # if one of them is zero, then the dot operation is multiple
|
||||
if ndim_a > 0 and ndim_b >= 2: #both of them is not zero, and one of them have at least two dims.
|
||||
perm = F.make_range(ndim_b)
|
||||
perm = perm[:-2] + (perm[-1],) + (perm[-2],)
|
||||
b = F.transpose(b, perm)
|
||||
b = F.transpose(b, perm) #using the result of make_range to transpose b.
|
||||
|
||||
if F.shape(a)[-1] != F.shape(b)[-1]:
|
||||
_raise_value_error('shapes are not aligned')
|
||||
a_aligned = F.reshape(a, (-1, F.shape(a)[-1]))
|
||||
a_aligned = F.reshape(a, (-1, F.shape(a)[-1])) #get the aligened model to adjust to the matmul_t.
|
||||
b_aligned = F.reshape(b, (-1, F.shape(b)[-1]))
|
||||
|
||||
res = _matmul_t(a_aligned, b_aligned)
|
||||
res = F.reshape(res, F.shape(a)[:-1] + F.shape(b)[:-1])
|
||||
res = _matmul_t(a_aligned, b_aligned) #get the res
|
||||
res = F.reshape(res, F.shape(a)[:-1] + F.shape(b)[:-1]) #beed to reshape the res
|
||||
return res
|
||||
|
||||
|
||||
|
|
@ -804,14 +804,14 @@ def outer(a, b):
|
|||
[6. 6. 6. 6.]
|
||||
[6. 6. 6. 6.]]
|
||||
"""
|
||||
_check_input_tensor(a, b)
|
||||
_check_input_tensor(a, b) #need to get the one dim array
|
||||
if F.rank(a) != 1:
|
||||
a = ravel(a)
|
||||
a = ravel(a)
|
||||
if F.rank(b) != 1:
|
||||
b = ravel(b)
|
||||
a = F.reshape(a, (F.shape(a)[0], 1))
|
||||
b = _expand(b, 2)
|
||||
return _matmul(a, b)
|
||||
a = F.reshape(a, (F.shape(a)[0], 1)) #reshape a to one dim
|
||||
b = _expand(b, 2) # the dim od b is 2
|
||||
return _matmul(a, b) #after these problem, return the _matmul result.
|
||||
|
||||
|
||||
def tensordot(a, b, axes=2):
|
||||
|
|
@ -870,7 +870,7 @@ def tensordot(a, b, axes=2):
|
|||
>>> print(output.shape)
|
||||
(5, 2)
|
||||
"""
|
||||
if F.rank(a)*F.rank(b) == 0 and axes == 0:
|
||||
if F.rank(a)*F.rank(b) == 0 and axes == 0: #for the tensor if the name is a
|
||||
return F.tensor_mul(a, b)
|
||||
return C.tensor_dot(a, b, axes)
|
||||
|
||||
|
|
@ -1030,29 +1030,29 @@ def average(x, axis=None, weights=None, returned=False):
|
|||
"""
|
||||
_check_input_tensor(x)
|
||||
if axis is not None:
|
||||
_check_axis_type(axis, True, True, False)
|
||||
axis = _canonicalize_axis(axis, x.ndim)
|
||||
_check_axis_type(axis, True, True, False) #ensure the type of anix is int, tuple and list
|
||||
axis = _canonicalize_axis(axis, x.ndim) #using the x.ndim to canonical form
|
||||
|
||||
x_avg = full((), nan, F.dtype(x))
|
||||
x_avg = full((), nan, F.dtype(x)) #get a new tensor filled with nan.
|
||||
sum_of_weights = None
|
||||
|
||||
if weights is None:
|
||||
x_avg = mean(x, axis)
|
||||
x_avg = mean(x, axis)
|
||||
sum_of_weights = compute_weights_for_mean(x, x_avg, axis)
|
||||
else:
|
||||
else: #if weights matrix is exist
|
||||
_check_input_tensor(weights)
|
||||
if x.shape == weights.shape:
|
||||
x_avg, sum_of_weights = comput_avg(x, axis, weights)
|
||||
elif F.rank(weights) == 1:
|
||||
x_avg, sum_of_weights = comput_avg(x, axis, weights) #if the shape is right ,then we can use the comput_avg to get the
|
||||
elif F.rank(weights) == 1: #if the rank of it is 1, then it must be int
|
||||
if not isinstance(axis, int):
|
||||
_raise_type_error("Axis must be specified when shapes of x and weights differ.")
|
||||
perm = _expanded_shape(x.ndim, weights.shape[0], axis)
|
||||
weights = weights.reshape(perm)
|
||||
weights = weights.reshape(perm) #get the perm to reshape the weights
|
||||
x_avg, sum_of_weights = comput_avg(x, axis, weights)
|
||||
else:
|
||||
_raise_type_error("Weights should be None, 1-D or the same shape as input x.")
|
||||
|
||||
if returned:
|
||||
if returned: #only the weights which ensure the demand will be returned
|
||||
if x_avg.shape != sum_of_weights.shape:
|
||||
sum_of_weights = _broadcast_to(sum_of_weights, sum_of_weights.shape, x_avg.shape, x_avg.ndim)
|
||||
return (x_avg, sum_of_weights)
|
||||
|
|
@ -1062,12 +1062,12 @@ def average(x, axis=None, weights=None, returned=False):
|
|||
def compute_weights_for_mean(x, x_avg, axis):
|
||||
"""Computes weights for np.average."""
|
||||
if axis is None:
|
||||
sum_of_weights = full((), x.size, F.dtype(x))
|
||||
sum_of_weights = full((), x.size, F.dtype(x)) #no axis means weights is none so the sum_of_weights is a new tensor
|
||||
else:
|
||||
fill_value = 1
|
||||
fill_value = 1 #else fill the tensor with 1
|
||||
if isinstance(axis, int) or (isinstance(axis, tuple) and F.tuple_len(axis) == 1):
|
||||
fill_value = x.shape[axis] if isinstance(axis, int) else x.shape[axis[0]]
|
||||
elif axis is None:
|
||||
elif axis is None: #update the tensor to get the tensor
|
||||
for sh in x.shape:
|
||||
fill_value *= sh
|
||||
else:
|
||||
|
|
@ -1127,7 +1127,7 @@ def matmul(x1, x2, dtype=None):
|
|||
[ 550. 620. 690. 760. 830.]
|
||||
[ 670. 756. 842. 928. 1014.]]]
|
||||
"""
|
||||
return C.matmul(x1, x2, dtype=dtype)
|
||||
return C.matmul(x1, x2, dtype=dtype) # this is from the composite
|
||||
|
||||
|
||||
def square(x, dtype=None):
|
||||
|
|
|
|||
Loading…
Reference in New Issue