Warrior_EA/AI_NETWORK.md

115 lines
5.7 KiB
Markdown
Raw Permalink Normal View History

# AI/ — neural network subsystem
The engine behind the four AI signal modules (`CSignalPAI`, `CSignalCONV`,
`CSignalLSTM`, `CSignalHYBRID`). It is a from-scratch MQL5 network — no external
ML runtime — with four interchangeable compute tiers behind one interface.
## File layout
`AI\Network.mqh` is the entry point and the only file anything outside `AI\`
includes. It holds **declarations only**: the nine core classes, the compile-time
constants, and the nested include chain that orders them. Each nested `#include`
sits exactly where its base class becomes visible, so that order is a dependency
graph, not a preference — do not reorder it.
| file | holds |
|---|---|
| `Network.mqh` | class declarations, tuning constants, include chain |
| `Network.cl` | OpenCL kernels (see below) |
| `NeuronPrimitives.mqh` | `CConnection` / `CArrayCon` — weight storage |
| `NeuronCPU.mqh` | `CNeuron` — pure-MQL5 dense neuron |
| `NeuronDirectML.mqh` | `CDirectMLMy` — DirectML + CPU-DLL bridge (`#import`ed DLLs) |
| `ArrayLayer.mqh` | `CArrayLayer` — the layer collection |
| `LayerDescription.mqh` | `CLayerDescription` — the topology descriptor |
| `BufferDouble.mqh` | `CBufferDouble` — host/device buffer |
| `NeuronOCLConvPool.mqh` | `CNeuronConvOCL`, `CNeuronPoolOCL` |
| `NeuronBatchNorm.mqh` | `CNeuronBatchNormOCL` — host-side batch norm, all tiers |
Method **bodies** live in `AI\Impl\`, included at the bottom of `Network.mqh`
after every declaration. This is a pure relocation — a body cannot run during
compilation, and every declaration it could reference is already visible above it.
| `AI\Impl\` | holds |
|---|---|
| `NeuronBase.mqh` | `CNeuronBase` — init, forward, gradients, activation, persistence |
| `NeuronConvPool.mqh` | `CNeuronConv`, `CNeuronPool` |
| `NeuronLSTM.mqh` | `CNeuronLSTM` — gate layers, BPTT |
| `Layer.mqh` | `CLayer` — element construction from a descriptor or a `.nnw` stream |
| `NetBuild.mqh` | `CNet` lifecycle: topology construction, OpenCL/DirectML init |
| `NetForward.mqh` | `feedForward`, `backProp`, `getResults`, logit adjustment |
| `NetPersistence.mqh` | `CNet::Save` / `CNet::Load` — the `.nnw` format |
| `NetWeights.mqh` | EMA blend, in-memory snapshot/restore, per-layer learning report |
| `NeuronOCLBase.mqh` | `CNeuronBaseOCL` |
| `NeuronOCLLSTM.mqh` | `CNeuronLSTMOCL` — sequence LSTM, fused kernels, BPTT caches |
## Compute tiers
Selected once at construction and logged; each layer runs entirely on one tier.
1. **OpenCL**`Network.cl`, compiled from the `#resource` at startup.
2. **DirectML**`WarriorDML.dll` (D3D12 compute).
3. **CPU DLL**`WarriorCPU.dll`, a threaded fallback. Thread count is fixed at
`CPU_THREADS_PER_NETWORK` per net rather than a share of the machine; see the
comment on that constant for why a machine-wide budget was wrong.
4. **Pure MQL5** — no DLL, no GPU. Single backtests run here so a Market build
(which strips every `#import`) is still able to infer.
A Market build (`WARRIOR_MARKET_BUILD`) compiles tiers 2 and 3 out entirely.
## Classes
- **`CNet`** — owns the layer stack, the forward/backward passes and `.nnw`
persistence. Note that a `.nnw` pins the **architecture**, not just the
weights: `Save` writes each layer's activation and `Load` restores it, so a
head activation changed in source only ever reaches a brand-new topology.
`EnforceOutputActivation()` re-asserts it after every load.
- **`CLayer` / `CArrayLayer`** — neuron containers; `CreateElementScaled()` is
the single construction point for every neuron type, from a descriptor or from
a file stream.
- **`CNeuronBase``CNeuron` / `CNeuronPool` / `CNeuronConv` / `CNeuronLSTM`** —
the pure-MQL5 family.
- **`CNeuronBaseOCL``CNeuronLSTMOCL` / `CNeuronConvOCL` / `CNeuronPoolOCL` /
`CNeuronBatchNormOCL`** — the accelerated family. Despite the `OCL` suffix
these also drive the DirectML and CPU-DLL tiers.
`ENUM_ACTIVATION` is `NONE, TANH, SIGMOID, PRELU`; `ENUM_OPTIMIZATION` is
`SGD, ADAM`. `NativeActivationCode()` translates to the int code the backends
share — and deliberately makes `NONE` fall through every backend switch.
## Network.cl
22 kernels in four groups: dense (`FeedForward`, `CaclOutputGradient`,
`CaclHiddenGradient`, `UpdateWeightsMomentum/Adam`), conv/pool (`FeedForwardConv`,
`FeedForwardProof`, `CalcHiddenGradientConv`, `CalcInputGradientProof`,
`UpdateWeightsConv*`), single-step LSTM (`LSTM_Gates`, `LSTM_State`,
`LSTM_*Gradient`) and sequence LSTM (`LSTM_SeqStep*`, `LSTM_UpdateWeights*`).
Two invariants worth knowing before touching them:
- `MIN_ACTIVATION_DERIVATIVE` (1.0e-3f) floors every LSTM gate derivative in both
gradient kernels. It is already there — do not "add" it.
- Kernel math is mirrored in `DirectML\WarriorCPU.cpp` and `WarriorDML.cpp`.
A change to one is a change to all three, or the tiers silently diverge.
## Usage
```
CNet *net = new CNet(topology); // CArrayObj of CLayerDescription
net.feedForward(inputs); // CArrayDouble
net.backProp(targets, weight);
net.getResults(out);
net.Save(path, ...); net.Load(path, ...);
```
The 3-output classification case is special-cased in `backProp`/`backPropOCL`:
it computes a joint softmax + cross-entropy gradient across all three neurons
rather than three independent per-neuron deltas.
## Known gaps
- No unit tests. Changes are validated by compile + a live era's `dW/W` line
(`CNet::LayerLearningReport`), which is the only reliable check that a layer is
actually receiving gradient in the assembled net.
- No dedicated SoftMax layer; the head is `Dense(SIGMOID)` with the gradient
short-circuited. See `REFACTOR_NOTES.md` §G for why, and for what would have to
move together to change it.