Chen's pr #2

Merged
longpi233 merged 1 commits from centre_master into master 2023-10-05 23:23:03 +08:00
20 changed files with 349 additions and 210 deletions

View File

@ -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;

View File

@ -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)) {}

View File

@ -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

View File

@ -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();
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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();

View File

@ -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;
}

View File

@ -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

View File

@ -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")

View File

@ -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])

View File

@ -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):

View File

@ -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))

View File

@ -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):