123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===- TrainingLogger.h - mlgo feature/reward logging ----------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // The design goals of the logger are:
- // - no dependencies that llvm doesn't already have.
- // - support streaming, so that we don't need to buffer data during compilation
- // - 0-decoding tensor values. Tensor values are potentially very large buffers
- // of scalars. Because of their potentially large size, avoiding
- // serialization/deserialization overhead is preferred.
- //
- // The simple logger produces an output of the form (each line item on its line)
- // - header: a json object describing the data that will follow.
- // - context: e.g. function name, for regalloc, or "default" for module-wide
- // optimizations like the inliner. This is the context to which the subsequent
- // data corresponds.
- // - observation number.
- // - tensor values - raw bytes of the tensors, in the order given in the header.
- // The values are in succession, i.e. no separator is found between successive
- // tensor values. At the end, there is a new line character.
- // - [score] - this is optional, and is present if it was present in the header.
- // Currently, for final rewards, we output "0" scores after each observation,
- // except for the last one.
- // <repeat>
- // The file should be read as binary, but the reason we use newlines is mostly
- // ease of debugging: the log can be opened in a text editor and, while tensor
- // values are inscrutable, at least the sequence of data can be easily observed.
- // Of course, the buffer of tensor values could contain '\n' bytes. A reader
- // should use the header information to know how much data to read for the
- // tensor values, and not use line information for that.
- //
- // An example reader, used for test, is available at
- // Analysis/models/log_reader.py
- //
- // Example:
- // {"features":[list of TensorSpecs], "score":<a tensor spec>}
- // {"context": "aFunction"}
- // {"observation": 0}
- // <bytes>
- // {"outcome": 0}
- // <bytes for the tensor corresponding to the "score" spec in the header>
- // {"observation": 1}
- // ...
- // {"context": "anotherFunction"}
- // {"observation": 0}
- // ...
- //
- #ifndef LLVM_ANALYSIS_UTILS_TRAININGLOGGER_H
- #define LLVM_ANALYSIS_UTILS_TRAININGLOGGER_H
- #include "llvm/Config/llvm-config.h"
- #include "llvm/ADT/StringMap.h"
- #include "llvm/Analysis/TensorSpec.h"
- #include "llvm/IR/LLVMContext.h"
- #include "llvm/Support/JSON.h"
- #include <memory>
- #include <optional>
- #include <vector>
- namespace llvm {
- /// Logging utility - given an ordered specification of features, and assuming
- /// a scalar reward, allow logging feature values and rewards.
- /// The assumption is that, for an event to be logged (i.e. a set of feature
- /// values and a reward), the user calls the log* API for each feature exactly
- /// once, providing the index matching the position in the feature spec list
- /// provided at construction. The example assumes the first feature's element
- /// type is float, the second is int64, and the reward is float:
- ///
- /// event 0:
- /// logFloatValue(0, ...)
- /// logInt64Value(1, ...)
- /// ...
- /// logFloatReward(...)
- /// event 1:
- /// logFloatValue(0, ...)
- /// logInt64Value(1, ...)
- /// ...
- /// logFloatReward(...)
- ///
- /// At the end, call print to generate the log.
- /// Alternatively, don't call logReward at the end of each event, just
- /// log{Float|Int32|Int64}FinalReward at the end.
- class Logger final {
- std::unique_ptr<raw_ostream> OS;
- const std::vector<TensorSpec> FeatureSpecs;
- const TensorSpec RewardSpec;
- const bool IncludeReward;
- StringMap<size_t> ObservationIDs;
- std::string CurrentContext;
- void writeHeader();
- void writeTensor(const TensorSpec &Spec, const char *RawData) {
- OS->write(RawData, Spec.getTotalTensorBufferSize());
- }
- void logRewardImpl(const char *RawData);
- public:
- /// Construct a Logger. If IncludeReward is false, then logReward or
- /// logFinalReward shouldn't be called, and the reward feature won't be
- /// printed out.
- /// NOTE: the FeatureSpecs are expected to be in the same order (i.e. have
- /// corresponding indices) with any MLModelRunner implementations
- /// corresponding to the model being trained/logged.
- Logger(std::unique_ptr<raw_ostream> OS,
- const std::vector<TensorSpec> &FeatureSpecs,
- const TensorSpec &RewardSpec, bool IncludeReward);
- void switchContext(StringRef Name);
- void startObservation();
- void endObservation();
- const std::string ¤tContext() const { return CurrentContext; }
- bool hasObservationInProgress() const {
- return ObservationIDs.find(CurrentContext) != ObservationIDs.end();
- }
- template <typename T> void logReward(T Value) {
- logRewardImpl(reinterpret_cast<const char *>(&Value));
- }
- void logTensorValue(size_t FeatureID, const char *RawData) {
- writeTensor(FeatureSpecs[FeatureID], RawData);
- }
- };
- } // namespace llvm
- #endif // LLVM_ANALYSIS_UTILS_TRAININGLOGGER_H
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|