Cleanup trainer features.

This commit is contained in:
Tomasz Sobczyk
2020-10-14 21:00:05 +02:00
committed by nodchip
parent 3041adb080
commit ea8eb415de
3 changed files with 256 additions and 251 deletions

View File

@@ -1,106 +1,109 @@
// NNUE evaluation function feature conversion class template
#ifndef _NNUE_TRAINER_FEATURES_FACTORIZER_H_
#ifndef _NNUE_TRAINER_FEATURES_FACTORIZER_H_
#define _NNUE_TRAINER_FEATURES_FACTORIZER_H_
#include "../../nnue_common.h"
#include "../trainer.h"
#include "nnue/nnue_common.h"
namespace Eval {
#include "nnue/trainer/trainer.h"
namespace NNUE {
// NNUE evaluation function feature conversion class template
namespace Eval::NNUE::Features {
namespace Features {
// Class template that converts input features into learning features
// By default, the learning feature is the same as the original input feature, and specialized as necessary
template <typename FeatureType>
class Factorizer {
public:
// Get the dimensionality of the learning feature
static constexpr IndexType GetDimensions() {
return FeatureType::kDimensions;
}
// Class template that converts input features into learning features
// By default, the learning feature is the same as the original input feature, and specialized as necessary
template <typename FeatureType>
class Factorizer {
public:
// Get the dimensionality of the learning feature
static constexpr IndexType GetDimensions() {
return FeatureType::kDimensions;
}
// Get index of learning feature and scale of learning rate
static void AppendTrainingFeatures(
IndexType base_index, std::vector<TrainingFeature>* training_features) {
// Get index of learning feature and scale of learning rate
static void AppendTrainingFeatures(
IndexType base_index, std::vector<TrainingFeature>* training_features) {
assert(base_index <FeatureType::kDimensions);
training_features->emplace_back(base_index);
}
};
assert(base_index <FeatureType::kDimensions);
training_features->emplace_back(base_index);
}
};
// Learning feature information
struct FeatureProperties {
bool active;
IndexType dimensions;
};
// Learning feature information
struct FeatureProperties {
bool active;
IndexType dimensions;
};
// Add the original input features to the learning features
template <typename FeatureType>
IndexType AppendBaseFeature(
FeatureProperties properties, IndexType base_index,
std::vector<TrainingFeature>* training_features) {
assert(properties.dimensions == FeatureType::kDimensions);
assert(base_index < FeatureType::kDimensions);
training_features->emplace_back(base_index);
return properties.dimensions;
}
// Add the original input features to the learning features
template <typename FeatureType>
IndexType AppendBaseFeature(
FeatureProperties properties, IndexType base_index,
std::vector<TrainingFeature>* training_features) {
// If the learning rate scale is not 0, inherit other types of learning features
template <typename FeatureType>
IndexType InheritFeaturesIfRequired(
IndexType index_offset, FeatureProperties properties, IndexType base_index,
std::vector<TrainingFeature>* training_features) {
if (!properties.active) {
return 0;
}
assert(properties.dimensions == Factorizer<FeatureType>::GetDimensions());
assert(base_index < FeatureType::kDimensions);
const auto start = training_features->size();
Factorizer<FeatureType>::AppendTrainingFeatures(
base_index, training_features);
for (auto i = start; i < training_features->size(); ++i) {
auto& feature = (*training_features)[i];
assert(feature.GetIndex() < Factorizer<FeatureType>::GetDimensions());
feature.ShiftIndex(index_offset);
}
return properties.dimensions;
}
// Return the index difference as needed, without adding learning features
// Call instead of InheritFeaturesIfRequired() if there are no corresponding features
IndexType SkipFeatures(FeatureProperties properties) {
if (!properties.active) {
return 0;
}
return properties.dimensions;
}
// Get the dimensionality of the learning feature
template <std::size_t N>
constexpr IndexType GetActiveDimensions(
const FeatureProperties (&properties)[N]) {
static_assert(N > 0, "");
IndexType dimensions = properties[0].dimensions;
for (std::size_t i = 1; i < N; ++i) {
if (properties[i].active) {
dimensions += properties[i].dimensions;
assert(properties.dimensions == FeatureType::kDimensions);
assert(base_index < FeatureType::kDimensions);
training_features->emplace_back(base_index);
return properties.dimensions;
}
}
return dimensions;
}
// get the number of elements in the array
template <typename T, std::size_t N>
constexpr std::size_t GetArrayLength(const T (&/*array*/)[N]) {
return N;
}
// If the learning rate scale is not 0, inherit other types of learning features
template <typename FeatureType>
IndexType InheritFeaturesIfRequired(
IndexType index_offset, FeatureProperties properties, IndexType base_index,
std::vector<TrainingFeature>* training_features) {
} // namespace Features
if (!properties.active) {
return 0;
}
} // namespace NNUE
assert(properties.dimensions == Factorizer<FeatureType>::GetDimensions());
assert(base_index < FeatureType::kDimensions);
} // namespace Eval
const auto start = training_features->size();
Factorizer<FeatureType>::AppendTrainingFeatures(
base_index, training_features);
for (auto i = start; i < training_features->size(); ++i) {
auto& feature = (*training_features)[i];
assert(feature.GetIndex() < Factorizer<FeatureType>::GetDimensions());
feature.ShiftIndex(index_offset);
}
return properties.dimensions;
}
// Return the index difference as needed, without adding learning features
// Call instead of InheritFeaturesIfRequired() if there are no corresponding features
IndexType SkipFeatures(FeatureProperties properties) {
if (!properties.active)
return 0;
return properties.dimensions;
}
// Get the dimensionality of the learning feature
template <std::size_t N>
constexpr IndexType GetActiveDimensions(
const FeatureProperties (&properties)[N]) {
static_assert(N > 0, "");
IndexType dimensions = properties[0].dimensions;
for (std::size_t i = 1; i < N; ++i) {
if (properties[i].active) {
dimensions += properties[i].dimensions;
}
}
return dimensions;
}
// get the number of elements in the array
template <typename T, std::size_t N>
constexpr std::size_t GetArrayLength(const T (&/*array*/)[N]) {
return N;
}
} // namespace Eval::NNUE::Features
#endif

View File

@@ -1,100 +1,105 @@
// Specialization for feature set of feature conversion class template of NNUE evaluation function
#ifndef _NNUE_TRAINER_FEATURES_FACTORIZER_FEATURE_SET_H_
#ifndef _NNUE_TRAINER_FEATURES_FACTORIZER_FEATURE_SET_H_
#define _NNUE_TRAINER_FEATURES_FACTORIZER_FEATURE_SET_H_
#include "../../features/feature_set.h"
#include "factorizer.h"
namespace Eval {
#include "nnue/features/feature_set.h"
namespace NNUE {
// Specialization for feature set of feature conversion class template of NNUE evaluation function
namespace Eval::NNUE::Features {
namespace Features {
// Class template that converts input features into learning features
// Specialization for FeatureSet
template <typename FirstFeatureType, typename... RemainingFeatureTypes>
class Factorizer<FeatureSet<FirstFeatureType, RemainingFeatureTypes...>> {
private:
using Head = Factorizer<FeatureSet<FirstFeatureType>>;
using Tail = Factorizer<FeatureSet<RemainingFeatureTypes...>>;
// Class template that converts input features into learning features
// Specialization for FeatureSet
template <typename FirstFeatureType, typename... RemainingFeatureTypes>
class Factorizer<FeatureSet<FirstFeatureType, RemainingFeatureTypes...>> {
private:
using Head = Factorizer<FeatureSet<FirstFeatureType>>;
using Tail = Factorizer<FeatureSet<RemainingFeatureTypes...>>;
public:
// number of dimensions of original input features
static constexpr IndexType kBaseDimensions =
FeatureSet<FirstFeatureType, RemainingFeatureTypes...>::kDimensions;
public:
// number of dimensions of original input features
static constexpr IndexType kBaseDimensions =
FeatureSet<FirstFeatureType, RemainingFeatureTypes...>::kDimensions;
// Get the dimensionality of the learning feature
static constexpr IndexType GetDimensions() {
return Head::GetDimensions() + Tail::GetDimensions();
}
// Get index of learning feature and scale of learning rate
static void AppendTrainingFeatures(
IndexType base_index, std::vector<TrainingFeature>* training_features,
IndexType base_dimensions = kBaseDimensions) {
assert(base_index < kBaseDimensions);
constexpr auto boundary = FeatureSet<RemainingFeatureTypes...>::kDimensions;
if (base_index < boundary) {
Tail::AppendTrainingFeatures(
base_index, training_features, base_dimensions);
} else {
const auto start = training_features->size();
Head::AppendTrainingFeatures(
base_index - boundary, training_features, base_dimensions);
for (auto i = start; i < training_features->size(); ++i) {
auto& feature = (*training_features)[i];
const auto index = feature.GetIndex();
assert(index < Head::GetDimensions() ||
(index >= base_dimensions &&
index < base_dimensions +
Head::GetDimensions() - Head::kBaseDimensions));
if (index < Head::kBaseDimensions) {
feature.ShiftIndex(Tail::kBaseDimensions);
} else {
feature.ShiftIndex(Tail::GetDimensions() - Tail::kBaseDimensions);
// Get the dimensionality of the learning feature
static constexpr IndexType GetDimensions() {
return Head::GetDimensions() + Tail::GetDimensions();
}
}
}
}
};
// Class template that converts input features into learning features
// Specialization when FeatureSet has one template argument
template <typename FeatureType>
class Factorizer<FeatureSet<FeatureType>> {
public:
// number of dimensions of original input features
static constexpr IndexType kBaseDimensions = FeatureType::kDimensions;
// Get index of learning feature and scale of learning rate
static void AppendTrainingFeatures(
IndexType base_index, std::vector<TrainingFeature>* training_features,
IndexType base_dimensions = kBaseDimensions) {
// Get the dimensionality of the learning feature
static constexpr IndexType GetDimensions() {
return Factorizer<FeatureType>::GetDimensions();
}
assert(base_index < kBaseDimensions);
// Get index of learning feature and scale of learning rate
static void AppendTrainingFeatures(
IndexType base_index, std::vector<TrainingFeature>* training_features,
IndexType base_dimensions = kBaseDimensions) {
assert(base_index < kBaseDimensions);
const auto start = training_features->size();
Factorizer<FeatureType>::AppendTrainingFeatures(
base_index, training_features);
for (auto i = start; i < training_features->size(); ++i) {
auto& feature = (*training_features)[i];
assert(feature.GetIndex() < Factorizer<FeatureType>::GetDimensions());
if (feature.GetIndex() >= kBaseDimensions) {
feature.ShiftIndex(base_dimensions - kBaseDimensions);
}
}
}
};
constexpr auto boundary = FeatureSet<RemainingFeatureTypes...>::kDimensions;
} // namespace Features
if (base_index < boundary) {
Tail::AppendTrainingFeatures(
base_index, training_features, base_dimensions);
}
else {
const auto start = training_features->size();
} // namespace NNUE
Head::AppendTrainingFeatures(
base_index - boundary, training_features, base_dimensions);
} // namespace Eval
for (auto i = start; i < training_features->size(); ++i) {
auto& feature = (*training_features)[i];
const auto index = feature.GetIndex();
assert(index < Head::GetDimensions() ||
(index >= base_dimensions &&
index < base_dimensions +
Head::GetDimensions() - Head::kBaseDimensions));
if (index < Head::kBaseDimensions) {
feature.ShiftIndex(Tail::kBaseDimensions);
}
else {
feature.ShiftIndex(Tail::GetDimensions() - Tail::kBaseDimensions);
}
}
}
}
};
// Class template that converts input features into learning features
// Specialization when FeatureSet has one template argument
template <typename FeatureType>
class Factorizer<FeatureSet<FeatureType>> {
public:
// number of dimensions of original input features
static constexpr IndexType kBaseDimensions = FeatureType::kDimensions;
// Get the dimensionality of the learning feature
static constexpr IndexType GetDimensions() {
return Factorizer<FeatureType>::GetDimensions();
}
// Get index of learning feature and scale of learning rate
static void AppendTrainingFeatures(
IndexType base_index, std::vector<TrainingFeature>* training_features,
IndexType base_dimensions = kBaseDimensions) {
assert(base_index < kBaseDimensions);
const auto start = training_features->size();
Factorizer<FeatureType>::AppendTrainingFeatures(
base_index, training_features);
for (auto i = start; i < training_features->size(); ++i) {
auto& feature = (*training_features)[i];
assert(feature.GetIndex() < Factorizer<FeatureType>::GetDimensions());
if (feature.GetIndex() >= kBaseDimensions) {
feature.ShiftIndex(base_dimensions - kBaseDimensions);
}
}
}
};
} // namespace Eval::NNUE::Features
#endif

View File

@@ -1,99 +1,96 @@
// Specialization of NNUE evaluation function feature conversion class template for HalfKP
#ifndef _NNUE_TRAINER_FEATURES_FACTORIZER_HALF_KP_H_
#ifndef _NNUE_TRAINER_FEATURES_FACTORIZER_HALF_KP_H_
#define _NNUE_TRAINER_FEATURES_FACTORIZER_HALF_KP_H_
#include "../../features/half_kp.h"
#include "../../features/p.h"
#include "../../features/half_relative_kp.h"
#include "factorizer.h"
namespace Eval {
#include "nnue/features/half_kp.h"
#include "nnue/features/p.h"
#include "nnue/features/half_relative_kp.h"
namespace NNUE {
// Specialization of NNUE evaluation function feature conversion class template for HalfKP
namespace Eval::NNUE::Features {
namespace Features {
// Class template that converts input features into learning features
// Specialization for HalfKP
template <Side AssociatedKing>
class Factorizer<HalfKP<AssociatedKing>> {
private:
using FeatureType = HalfKP<AssociatedKing>;
// Class template that converts input features into learning features
// Specialization for HalfKP
template <Side AssociatedKing>
class Factorizer<HalfKP<AssociatedKing>> {
private:
using FeatureType = HalfKP<AssociatedKing>;
// The maximum value of the number of indexes whose value is 1 at the same time among the feature values
static constexpr IndexType kMaxActiveDimensions =
FeatureType::kMaxActiveDimensions;
// The maximum value of the number of indexes whose value is 1 at the same time among the feature values
static constexpr IndexType kMaxActiveDimensions =
FeatureType::kMaxActiveDimensions;
// Type of learning feature
enum TrainingFeatureType {
kFeaturesHalfKP,
kFeaturesHalfK,
kFeaturesP,
kFeaturesHalfRelativeKP,
kNumTrainingFeatureTypes,
};
// Type of learning feature
enum TrainingFeatureType {
kFeaturesHalfKP,
kFeaturesHalfK,
kFeaturesP,
kFeaturesHalfRelativeKP,
kNumTrainingFeatureTypes,
};
// Learning feature information
static constexpr FeatureProperties kProperties[] = {
// kFeaturesHalfKP
{true, FeatureType::kDimensions},
// kFeaturesHalfK
{true, SQUARE_NB},
// kFeaturesP
{true, Factorizer<P>::GetDimensions()},
// kFeaturesHalfRelativeKP
{true, Factorizer<HalfRelativeKP<AssociatedKing>>::GetDimensions()},
};
// Learning feature information
static constexpr FeatureProperties kProperties[] = {
// kFeaturesHalfKP
{true, FeatureType::kDimensions},
// kFeaturesHalfK
{true, SQUARE_NB},
// kFeaturesP
{true, Factorizer<P>::GetDimensions()},
// kFeaturesHalfRelativeKP
{true, Factorizer<HalfRelativeKP<AssociatedKing>>::GetDimensions()},
};
static_assert(GetArrayLength(kProperties) == kNumTrainingFeatureTypes, "");
static_assert(GetArrayLength(kProperties) == kNumTrainingFeatureTypes, "");
public:
// Get the dimensionality of the learning feature
static constexpr IndexType GetDimensions() {
return GetActiveDimensions(kProperties);
}
public:
// Get the dimensionality of the learning feature
static constexpr IndexType GetDimensions() {
return GetActiveDimensions(kProperties);
}
// Get index of learning feature and scale of learning rate
static void AppendTrainingFeatures(
IndexType base_index, std::vector<TrainingFeature>* training_features) {
// kFeaturesHalfKP
IndexType index_offset = AppendBaseFeature<FeatureType>(
kProperties[kFeaturesHalfKP], base_index, training_features);
// Get index of learning feature and scale of learning rate
static void AppendTrainingFeatures(
IndexType base_index, std::vector<TrainingFeature>* training_features) {
const auto sq_k = static_cast<Square>(base_index / PS_END);
const auto p = static_cast<IndexType>(base_index % PS_END);
// kFeaturesHalfK
{
const auto& properties = kProperties[kFeaturesHalfK];
if (properties.active) {
training_features->emplace_back(index_offset + sq_k);
index_offset += properties.dimensions;
}
}
// kFeaturesP
index_offset += InheritFeaturesIfRequired<P>(
index_offset, kProperties[kFeaturesP], p, training_features);
// kFeaturesHalfRelativeKP
if (p >= PS_W_PAWN) {
index_offset += InheritFeaturesIfRequired<HalfRelativeKP<AssociatedKing>>(
index_offset, kProperties[kFeaturesHalfRelativeKP],
HalfRelativeKP<AssociatedKing>::MakeIndex(sq_k, p),
training_features);
} else {
index_offset += SkipFeatures(kProperties[kFeaturesHalfRelativeKP]);
}
// kFeaturesHalfKP
IndexType index_offset = AppendBaseFeature<FeatureType>(
kProperties[kFeaturesHalfKP], base_index, training_features);
assert(index_offset == GetDimensions());
}
};
const auto sq_k = static_cast<Square>(base_index / PS_END);
const auto p = static_cast<IndexType>(base_index % PS_END);
template <Side AssociatedKing>
constexpr FeatureProperties Factorizer<HalfKP<AssociatedKing>>::kProperties[];
// kFeaturesHalfK
{
const auto& properties = kProperties[kFeaturesHalfK];
if (properties.active) {
training_features->emplace_back(index_offset + sq_k);
index_offset += properties.dimensions;
}
}
} // namespace Features
// kFeaturesP
index_offset += InheritFeaturesIfRequired<P>(
index_offset, kProperties[kFeaturesP], p, training_features);
// kFeaturesHalfRelativeKP
if (p >= PS_W_PAWN) {
index_offset += InheritFeaturesIfRequired<HalfRelativeKP<AssociatedKing>>(
index_offset, kProperties[kFeaturesHalfRelativeKP],
HalfRelativeKP<AssociatedKing>::MakeIndex(sq_k, p),
training_features);
}
else {
index_offset += SkipFeatures(kProperties[kFeaturesHalfRelativeKP]);
}
} // namespace NNUE
assert(index_offset == GetDimensions());
}
};
} // namespace Eval
template <Side AssociatedKing>
constexpr FeatureProperties Factorizer<HalfKP<AssociatedKing>>::kProperties[];
} // namespace Eval::NNUE::Features
#endif