-
Notifications
You must be signed in to change notification settings - Fork 53
[2026春季][T2-2-1]ChaoticLuna #188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
df7308c
5367598
fece48d
5551fe3
0c5aa44
41cb406
383657e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| #pragma once | ||
|
|
||
| #include <memory> | ||
| #include <optional> | ||
| #include <string> | ||
| #include <utility> | ||
| #include <vector> | ||
|
|
||
| #include "infini_train/include/autograd/function.h" | ||
| #include "infini_train/include/generator.h" | ||
|
|
||
| namespace infini_train { | ||
| class Tensor; | ||
| } | ||
|
|
||
| namespace infini_train::autograd { | ||
|
|
||
| class Dropout final : public Function { | ||
| public: | ||
| static constexpr char kType[] = "DropoutFunction"; | ||
|
|
||
| Dropout(double p, std::optional<Generator> generator) : Function(kType), p_(p), generator_(std::move(generator)) {} | ||
|
|
||
| std::vector<std::shared_ptr<Tensor>> Forward(const std::vector<std::shared_ptr<Tensor>> &input_tensors) override; | ||
| void SetupContext(const std::vector<std::shared_ptr<Tensor>> &input_tensors, | ||
| const std::vector<std::shared_ptr<Tensor>> &output_tensors) override; | ||
| std::vector<std::shared_ptr<Tensor>> Backward(const std::vector<std::shared_ptr<Tensor>> &grad_outputs) override; | ||
|
|
||
| private: | ||
| double p_ = 0.0; | ||
| std::optional<Generator> generator_; | ||
| }; | ||
|
|
||
| } // namespace infini_train::autograd | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| #pragma once | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个文件不是公共接口,只有两个cpu算子用到了,可以挪到kernels/cpu/目录下 |
||
|
|
||
| // Host-side uniform and normal distributions for generators exposing random() | ||
| // and random64(). Box-Muller's second sample is cached when supported by the generator. | ||
|
|
||
| #include <cmath> | ||
| #include <cstdint> | ||
| #include <limits> | ||
| #include <numbers> | ||
| #include <optional> | ||
| #include <type_traits> | ||
|
|
||
| #include "glog/logging.h" | ||
|
|
||
| namespace infini_train::common::cpu { | ||
|
|
||
| template <typename T> struct uniform_real_distribution { | ||
| uniform_real_distribution(T from, T to) : from_(from), to_(to) { | ||
| CHECK_LE(from, to); | ||
| CHECK_LE(to - from, std::numeric_limits<T>::max()); | ||
| } | ||
|
|
||
| uniform_real_distribution(const uniform_real_distribution &) = default; | ||
| uniform_real_distribution &operator=(const uniform_real_distribution &) = delete; | ||
|
|
||
| template <typename RNG> T operator()(RNG *generator) const { | ||
| if constexpr (std::is_same_v<T, double>) { | ||
| return transform(generator->random64()); | ||
| } else { | ||
| return transform(generator->random()); | ||
| } | ||
| } | ||
|
|
||
| private: | ||
| T from_; | ||
| T to_; | ||
|
|
||
| template <typename V> T transform(V val) const { | ||
| constexpr auto MASK = static_cast<V>((static_cast<uint64_t>(1) << std::numeric_limits<T>::digits) - 1); | ||
| constexpr auto DIVISOR = static_cast<T>(1) / (static_cast<uint64_t>(1) << std::numeric_limits<T>::digits); | ||
| T x = (val & MASK) * DIVISOR; | ||
| return x * (to_ - from_) + from_; | ||
| } | ||
| }; | ||
|
|
||
| template <typename RNG, typename = decltype(&RNG::next_double_normal_sample), | ||
| typename = decltype(&RNG::set_next_double_normal_sample)> | ||
| bool maybe_get_next_normal_sample(RNG *generator, double *ret) { | ||
| const auto sample = generator->next_double_normal_sample(); | ||
| if (!sample.has_value()) { | ||
| return false; | ||
| } | ||
| *ret = sample.value(); | ||
| generator->set_next_double_normal_sample(std::nullopt); | ||
| return true; | ||
| } | ||
|
|
||
| template <typename RNG, typename = decltype(&RNG::next_float_normal_sample), | ||
| typename = decltype(&RNG::set_next_float_normal_sample)> | ||
| bool maybe_get_next_normal_sample(RNG *generator, float *ret) { | ||
| const auto sample = generator->next_float_normal_sample(); | ||
| if (!sample.has_value()) { | ||
| return false; | ||
| } | ||
| *ret = sample.value(); | ||
| generator->set_next_float_normal_sample(std::nullopt); | ||
| return true; | ||
| } | ||
|
|
||
| // Fallback: RNG without cache support never has a cached sample. | ||
| template <typename RNG> bool maybe_get_next_normal_sample(RNG * /*generator*/, void * /*ret*/) { return false; } | ||
|
|
||
| template <typename RNG, typename = decltype(&RNG::set_next_double_normal_sample)> | ||
| void maybe_set_next_normal_sample(RNG *generator, const double *cache) { | ||
| generator->set_next_double_normal_sample(*cache); | ||
| } | ||
|
|
||
| template <typename RNG, typename = decltype(&RNG::set_next_float_normal_sample)> | ||
| void maybe_set_next_normal_sample(RNG *generator, const float *cache) { | ||
| generator->set_next_float_normal_sample(*cache); | ||
| } | ||
|
|
||
| // Fallback: RNG without cache support discards the second sample. | ||
| template <typename RNG> void maybe_set_next_normal_sample(RNG * /*generator*/, const void * /*cache*/) {} | ||
|
|
||
| template <typename T> struct normal_distribution { | ||
| normal_distribution(T mean, T stdv) : mean_(mean), stdv_(stdv) { CHECK_GE(stdv, static_cast<T>(0)); } | ||
|
|
||
| normal_distribution(const normal_distribution &) = default; | ||
| normal_distribution &operator=(const normal_distribution &) = delete; | ||
|
|
||
| template <typename RNG> T operator()(RNG *generator) const { | ||
| T ret; | ||
| if (maybe_get_next_normal_sample(generator, &ret)) { | ||
| return ret * stdv_ + mean_; | ||
| } | ||
|
|
||
| uniform_real_distribution<T> uniform(static_cast<T>(0), static_cast<T>(1)); | ||
| const T u1 = uniform(generator); | ||
| const T u2 = uniform(generator); | ||
|
|
||
| const T r = std::sqrt(static_cast<T>(-2.0) * std::log1p(-u2)); | ||
| constexpr T kTwoPi = static_cast<T>(2.0 * std::numbers::pi_v<double>); | ||
| const T theta = kTwoPi * u1; | ||
| const T sample = r * std::sin(theta); | ||
|
|
||
| maybe_set_next_normal_sample(generator, &sample); | ||
|
|
||
| ret = r * std::cos(theta); | ||
| return ret * stdv_ + mean_; | ||
| } | ||
|
|
||
| private: | ||
| T mean_; | ||
| T stdv_; | ||
| }; | ||
|
|
||
| } // namespace infini_train::common::cpu | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| #pragma once | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个头文件是公开的,建议区分 generator.h 和 generator_impl.h 文件,避免把 GeneratorImpl 暴露给外部。 |
||
|
|
||
| #include <cstdint> | ||
| #include <memory> | ||
| #include <mutex> | ||
| #include <optional> | ||
| #include <stdexcept> | ||
| #include <utility> | ||
|
|
||
| #include "infini_train/include/device.h" | ||
|
|
||
| namespace infini_train { | ||
|
|
||
| class Tensor; | ||
|
|
||
| namespace detail { | ||
|
|
||
| // Validates the common Tensor contract for serialized RNG states. | ||
| void check_rng_state(const Tensor &state); | ||
|
|
||
| } // namespace detail | ||
|
|
||
| // Base interface for device-specific random number generators. | ||
| class GeneratorImpl { | ||
| public: | ||
| explicit GeneratorImpl(Device device) : device_(device) {} | ||
| virtual ~GeneratorImpl() = default; | ||
|
|
||
| GeneratorImpl(const GeneratorImpl &other) = delete; | ||
| GeneratorImpl(GeneratorImpl &&other) = delete; | ||
| GeneratorImpl &operator=(const GeneratorImpl &other) = delete; | ||
| GeneratorImpl &operator=(GeneratorImpl &&other) = delete; | ||
|
|
||
| virtual void set_current_seed(uint64_t seed) = 0; | ||
| virtual uint64_t current_seed() const = 0; | ||
| virtual uint64_t seed() = 0; | ||
| virtual void set_state(const Tensor &state) = 0; | ||
| virtual std::shared_ptr<Tensor> get_state() const = 0; | ||
|
|
||
| std::shared_ptr<GeneratorImpl> clone() const { return std::shared_ptr<GeneratorImpl>(clone_impl()); } | ||
|
|
||
| Device device() const { return device_; } | ||
|
|
||
| // Callers must lock this mutex when an operation spans multiple generator calls. | ||
| std::mutex mutex_; | ||
|
|
||
| protected: | ||
| Device device_; | ||
|
|
||
| virtual GeneratorImpl *clone_impl() const = 0; | ||
| }; | ||
|
|
||
| // A lightweight handle with shared-copy semantics. Use clone() for an independent state. | ||
| class Generator { | ||
| public: | ||
| static constexpr uint64_t kDefaultSeed = 67280421310721; | ||
|
|
||
| Generator() = default; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 可以删除 undefined Generator 状态,每个构造出的 Generator 都保证 impl_ 非空 |
||
|
|
||
| explicit Generator(std::shared_ptr<GeneratorImpl> impl); | ||
|
|
||
| Generator(const Generator &) = default; | ||
| Generator &operator=(const Generator &) = default; | ||
| Generator(Generator &&) = default; | ||
| Generator &operator=(Generator &&) = default; | ||
|
|
||
| ~Generator() = default; | ||
|
|
||
| void set_current_seed(uint64_t seed) const { impl_->set_current_seed(seed); } | ||
| uint64_t current_seed() const { return impl_->current_seed(); } | ||
| uint64_t seed() { return impl_->seed(); } | ||
|
|
||
| void set_state(const Tensor &state); | ||
| std::shared_ptr<Tensor> get_state() const; | ||
|
|
||
| Device device() const { return impl_->device(); } | ||
|
|
||
| Generator clone() const { return Generator(impl_->clone()); } | ||
|
|
||
| std::mutex &mutex() const { return impl_->mutex_; } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这个接口不用暴露 |
||
|
|
||
| // Prefer check_generator<T>(); this unchecked accessor assumes a matching backend. | ||
| template <typename T> T *get() const { return static_cast<T *>(impl_.get()); } | ||
|
|
||
| GeneratorImpl *unsafeGetGeneratorImpl() const { return impl_.get(); } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. get()和unsafeGetGeneratorImpl() 等一系列接口作为 public API 不合理,它相当于为了实现内部 check_generator(),把整个封装打穿了。建议删除这里的不安全接口 其中公开的 get() 和 unsafeGetGeneratorImpl() 可以删除,其他类型检查和默认 Generator 解析移动到 generator_impl.h,通过私有 friend accessor 获取实现 class GeneratorImpl {
private:
friend class GeneratorAccessor;
};
class GeneratorAccessor {
public:
static GeneratorImpl &Get(const Generator &generator) {
if (!generator.impl_) {
throw std::invalid_argument("Undefined Generator");
}
return *generator.impl_;
}
static std::mutex &Mutex(const Generator &generator) {
return Get(generator).mutex_;
}
};
template <typename T>
T &CheckedGeneratorImpl(const Generator &generator,
const Device &expected_device) {
static_assert(std::is_base_of_v<GeneratorImpl, T>);
auto &base = GeneratorAccessor::Get(generator);
if (base.device() != expected_device) {
throw std::invalid_argument("Generator device mismatch");
}
auto *typed = dynamic_cast<T *>(&base);
if (typed == nullptr) {
throw std::invalid_argument("Generator backend mismatch");
}
return *typed;
}
template <class Impl, class... Args>
Generator MakeGenerator(Args &&...args);
void CheckRngState(const Tensor &state);类似这样。注意命名统一遵循 Google 风格。
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 这里 MakeGenerator() 需要构造私有 Generator,也可以让 GeneratorAccessor 提供内部构造函数。 |
||
| bool defined() const { return impl_ != nullptr; } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 不允许空值初始化的话这个defined()判断函数可以删。虽然 PyTorch 的 C++ API 有这个函数,但应该是为了延迟初始化或者支持 Python None,我们这里暂时不需要,后续有需要再添加。后面使用处可以替换成has_value() |
||
|
|
||
| friend bool operator==(const Generator &a, const Generator &b) { return a.impl_ == b.impl_; } | ||
| friend bool operator!=(const Generator &a, const Generator &b) { return !(a == b); } | ||
|
|
||
| private: | ||
| std::shared_ptr<GeneratorImpl> impl_; | ||
| }; | ||
|
|
||
| // Internal factory for backend implementations. | ||
| template <class Impl, class... Args> Generator make_generator(Args &&...args) { | ||
| return Generator(std::make_shared<Impl>(std::forward<Args>(args)...)); | ||
| } | ||
|
|
||
| template <typename T> T *check_generator(const Generator &generator) { | ||
| if (!generator.defined()) { | ||
| throw std::invalid_argument("Generator with undefined implementation is not allowed"); | ||
| } | ||
| if (T::device_type() != generator.device().type()) { | ||
| throw std::invalid_argument("Generator device type does not match the requested backend"); | ||
| } | ||
|
|
||
| auto *impl = dynamic_cast<T *>(generator.unsafeGetGeneratorImpl()); | ||
| if (impl == nullptr) { | ||
| throw std::invalid_argument("Generator implementation does not match the requested backend"); | ||
| } | ||
| return impl; | ||
| } | ||
|
|
||
| template <typename T> | ||
| T *get_generator_or_default(const std::optional<Generator> &generator, const Generator &default_generator) { | ||
| return generator.has_value() && generator->defined() ? check_generator<T>(*generator) | ||
| : check_generator<T>(default_generator); | ||
| } | ||
|
|
||
| // Creates a generator for the requested device without exposing its backend implementation. | ||
| Generator CreateGenerator(const Device &device, uint64_t seed = Generator::kDefaultSeed); | ||
|
|
||
| // Returns the lazily initialized default generator for the requested device. | ||
| const Generator &GetDefaultGenerator(const Device &device); | ||
|
|
||
| // Reset the default generators for all enabled devices. | ||
| void manual_seed(uint64_t seed); | ||
|
|
||
| } // namespace infini_train | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,13 +5,15 @@ | |
| #include <memory> | ||
| #include <optional> | ||
| #include <random> | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 删除无用的<random>,删除空行 |
||
| #include <vector> | ||
|
|
||
| #include "Eigen/Dense" | ||
| #include "glog/logging.h" | ||
|
|
||
| #include "infini_train/include/datatype.h" | ||
| #include "infini_train/include/device.h" | ||
| #include "infini_train/include/generator.h" | ||
| #include "infini_train/include/scalar.h" | ||
|
|
||
| namespace infini_train { | ||
|
|
@@ -74,6 +76,8 @@ class Tensor : public std::enable_shared_from_this<Tensor> { | |
| void *DataPtr(); | ||
| const void *DataPtr() const; | ||
|
|
||
| bool defined() const { return buffer_ != nullptr; } | ||
|
|
||
| size_t SizeInBytes() const; | ||
|
|
||
| const std::vector<int64_t> &Dims() const; | ||
|
|
@@ -151,7 +155,7 @@ class Tensor : public std::enable_shared_from_this<Tensor> { | |
|
|
||
| // distribution | ||
| std::shared_ptr<Tensor> Uniform(float from = 0.0f, float to = 1.0f, | ||
| std::optional<std::mt19937> generator = std::nullopt); | ||
| std::optional<Generator> generator = std::nullopt); | ||
|
|
||
| std::shared_ptr<Tensor> Matmul(const std::shared_ptr<Tensor> &other); | ||
| std::shared_ptr<Tensor> Outer(const std::shared_ptr<Tensor> &other); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
训练入口 examples 应该调用 ManualSeed() 。考虑到多线程并发,应该在 main() 中完成环境初始化后、创建训练线程之前调用一次(现在的 InitAllEnv 调用之后)同时删除 Train() 里的注释调用。