154 lines
5 KiB
MQL5
154 lines
5 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| KronosSampling.mqh |
|
||
|
|
//| MMQ — Muhammad Minhas Qamar |
|
||
|
|
//| www.mql5.com |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "MMQ — Muhammad Minhas Qamar"
|
||
|
|
#property link "https://www.mql5.com"
|
||
|
|
#property version "1.00"
|
||
|
|
|
||
|
|
#ifndef KRONOS_SAMPLING_MQH
|
||
|
|
#define KRONOS_SAMPLING_MQH
|
||
|
|
|
||
|
|
#define KR_NEG_INF (-1.0e308)
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| In-place softmax over an array, with the -inf entries mapped to |
|
||
|
|
//| zero probability and the usual max-subtraction for stability. |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void Softmax(double &x[])
|
||
|
|
{
|
||
|
|
int n = ArraySize(x);
|
||
|
|
double mx = KR_NEG_INF;
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
if(x[i] > mx)
|
||
|
|
mx = x[i];
|
||
|
|
double s = 0.0;
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
{
|
||
|
|
x[i] = (x[i] <= KR_NEG_INF * 0.5) ? 0.0 : MathExp(x[i] - mx);
|
||
|
|
s += x[i];
|
||
|
|
}
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
x[i] /= s;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Return the index order that sorts key[] in descending value. |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ArgsortDesc(const double &key[], int &order[])
|
||
|
|
{
|
||
|
|
int n = ArraySize(key);
|
||
|
|
ArrayResize(order, n);
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
order[i] = i;
|
||
|
|
for(int i = 0; i < n - 1; i++)
|
||
|
|
for(int j = 0; j < n - 1 - i; j++)
|
||
|
|
if(key[order[j]] < key[order[j + 1]])
|
||
|
|
{ int t = order[j]; order[j] = order[j + 1]; order[j + 1] = t; }
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Top-k then top-p filtering. Removed logits are set to -inf, at |
|
||
|
|
//| least one token is kept, and the first token that crosses the |
|
||
|
|
//| cumulative top-p threshold is itself kept. |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void TopKTopPFilter(double &logits[], int top_k, double top_p)
|
||
|
|
{
|
||
|
|
int n = ArraySize(logits);
|
||
|
|
|
||
|
|
if(top_k > 0)
|
||
|
|
{
|
||
|
|
int k = (int)MathMin(MathMax(top_k, 1), n);
|
||
|
|
double tmp[];
|
||
|
|
ArrayCopy(tmp, logits);
|
||
|
|
int order[];
|
||
|
|
ArgsortDesc(tmp, order);
|
||
|
|
double kth = logits[order[k - 1]];
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
if(logits[i] < kth)
|
||
|
|
logits[i] = KR_NEG_INF;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(top_p < 1.0)
|
||
|
|
{
|
||
|
|
int order[];
|
||
|
|
ArgsortDesc(logits, order);
|
||
|
|
double probs[];
|
||
|
|
ArrayResize(probs, n);
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
probs[i] = logits[order[i]];
|
||
|
|
Softmax(probs);
|
||
|
|
|
||
|
|
bool remove_sorted[];
|
||
|
|
ArrayResize(remove_sorted, n);
|
||
|
|
double cum = 0.0;
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
{
|
||
|
|
cum += probs[i];
|
||
|
|
remove_sorted[i] = (cum > top_p);
|
||
|
|
}
|
||
|
|
for(int i = n - 1; i > 0; i--)
|
||
|
|
remove_sorted[i] = remove_sorted[i - 1];
|
||
|
|
remove_sorted[0] = false;
|
||
|
|
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
if(remove_sorted[i])
|
||
|
|
logits[order[i]] = KR_NEG_INF;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Index of the largest element (greedy pick). |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int Argmax(const double &x[])
|
||
|
|
{
|
||
|
|
int n = ArraySize(x), best = 0;
|
||
|
|
double bv = x[0];
|
||
|
|
for(int i = 1; i < n; i++)
|
||
|
|
if(x[i] > bv)
|
||
|
|
{
|
||
|
|
bv = x[i];
|
||
|
|
best = i;
|
||
|
|
}
|
||
|
|
return best;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Draw one index from a probability array (inverse-CDF sampling). |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int MultinomialDraw(const double &probs[])
|
||
|
|
{
|
||
|
|
double r = (double)MathRand() / 32767.0;
|
||
|
|
double c = 0.0;
|
||
|
|
int n = ArraySize(probs);
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
{
|
||
|
|
c += probs[i];
|
||
|
|
if(r <= c)
|
||
|
|
return i;
|
||
|
|
}
|
||
|
|
return n - 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Full decode step: temperature scale, top-k/top-p filter, |
|
||
|
|
//| softmax, then sample. greedy=true returns the argmax instead. |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int SampleFromLogits(const double &logits_in[], double T, int top_k, double top_p, bool greedy)
|
||
|
|
{
|
||
|
|
double l[];
|
||
|
|
ArrayCopy(l, logits_in);
|
||
|
|
int n = ArraySize(l);
|
||
|
|
for(int i = 0; i < n; i++)
|
||
|
|
l[i] /= T;
|
||
|
|
if(top_k > 0 || top_p < 1.0)
|
||
|
|
TopKTopPFilter(l, top_k, top_p);
|
||
|
|
double probs[];
|
||
|
|
ArrayCopy(probs, l);
|
||
|
|
Softmax(probs);
|
||
|
|
return greedy ? Argmax(probs) : MultinomialDraw(probs);
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif // KRONOS_SAMPLING_MQH
|
||
|
|
//+------------------------------------------------------------------+
|