Skip to content

Commit 73d8ecb

Browse files
committed
Add better statistics
1 parent 62ac268 commit 73d8ecb

File tree

1 file changed

+80
-41
lines changed

1 file changed

+80
-41
lines changed

examples/imatrix/imatrix.cpp

Lines changed: 80 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
#include "arg.h"
2-
#include "common.h"
3-
#include "log.h"
4-
#include "llama.h"
5-
1+
#include <algorithm>
62
#include <chrono>
73
#include <cmath>
84
#include <cstdio>
95
#include <cstring>
106
#include <ctime>
11-
#include <thread>
12-
#include <mutex>
13-
#include <vector>
147
#include <fstream>
8+
#include <mutex>
9+
#include <numeric>
10+
#include <thread>
1511
#include <unordered_map>
16-
#include <algorithm>
12+
#include <vector>
13+
14+
#include "arg.h"
15+
#include "common.h"
16+
#include "llama.h"
17+
#include "log.h"
1718

1819
#if defined(_MSC_VER)
1920
#pragma warning(disable: 4244 4267) // possible loss of data
@@ -33,10 +34,18 @@ struct Stats {
3334
int ncall = 0;
3435
};
3536

36-
struct Tally {
37+
struct tensor_statistics {
3738
std::string tensor;
38-
double bias = 0;
39-
int count = 0;
39+
float total = 0;
40+
float mean = 0;
41+
float max = 0;
42+
float min = 0;
43+
float stddev = 0;
44+
float cv = 0;
45+
float zd = 0;
46+
float active = 0;
47+
float entropy = 0;
48+
int elements = 0;
4049
};
4150

4251
class IMatrixCollector {
@@ -45,7 +54,7 @@ class IMatrixCollector {
4554
void set_params(common_params params) { m_params = std::move(params); }
4655
bool collect_imatrix(struct ggml_tensor * t, bool ask, void * user_data);
4756
void save_imatrix(int ncall = -1) const;
48-
bool load_imatrix(const char * fname, std::vector<Tally> * tally = nullptr);
57+
bool load_imatrix(const char * fname, std::vector<tensor_statistics> * tstats = nullptr);
4958
private:
5059
std::unordered_map<std::string, Stats> m_stats;
5160
common_params m_params;
@@ -323,7 +332,7 @@ void IMatrixCollector::save_imatrix(int ncall) const {
323332
LOG_DBGV(1, "%s: stored collected data after %d chunks in %s\n", __func__, m_last_call, fname.c_str());
324333
}
325334

326-
bool IMatrixCollector::load_imatrix(const char * fname, std::vector<Tally> * tally) {
335+
bool IMatrixCollector::load_imatrix(const char * fname, std::vector<tensor_statistics> * ts) {
327336
std::ifstream in(fname, std::ios::binary);
328337
if (!in) {
329338
LOG_ERR("%s: failed to open %s\n",__func__, fname);
@@ -370,21 +379,58 @@ bool IMatrixCollector::load_imatrix(const char * fname, std::vector<Tally> * tal
370379
}
371380

372381
// Recreate the state as expected by save_imatrix(), and correct for weighted sum.
373-
double total = 0;
382+
std::vector<float> activations;
383+
activations.reserve(nval);
384+
374385
for (int i = 0; i < nval; i++) {
375386
e.values[i] += tmp[i];
376387
e.counts[i] += ncall;
377-
const double avg_sq = (1.0 * e.values[i]) / e.counts[i];
378-
total += avg_sq;
388+
activations.push_back(e.values[i] / static_cast<float>(e.counts[i]));
379389
}
380390
e.ncall += ncall;
381391

382-
if (tally) {
383-
tally->emplace_back();
384-
auto & [tensor, bias, count] = (*tally)[i];
392+
if (ts) {
393+
float total_bias = std::accumulate(activations.begin(), activations.end(), 0.0f);
394+
float max_bias = * std::max_element(activations.begin(), activations.end());
395+
float min_bias = * std::min_element(activations.begin(), activations.end());
396+
float mean_bias = total_bias / activations.size();
397+
float sq_total_bias = std::inner_product(activations.begin(), activations.end(), activations.begin(), 0.0f);
398+
float dev = std::sqrt((sq_total_bias / activations.size()) - (mean_bias * mean_bias));
399+
float rmsd = mean_bias > 0.0f ? dev / mean_bias : 0.0f;
400+
401+
float threshold = 1e-6f;
402+
int inactive_count = std::count_if(activations.begin(), activations.end(), [threshold](const float v) { return fabs(v) < threshold; });
403+
float active_ratio = 1 - (static_cast<float>(inactive_count) / activations.size());
404+
405+
float ent = 0.0f;
406+
if (total_bias > 0) {
407+
for (auto act : activations) {
408+
if (float p = act / total_bias; p > 0) {
409+
ent -= p* std::log2(p);
410+
}
411+
}
412+
}
413+
414+
int z_score = 0;
415+
for (auto act : activations) {
416+
if (float p = (act - mean_bias) / dev; p > 1) {
417+
z_score++;
418+
}
419+
}
420+
421+
ts->emplace_back();
422+
auto & [tensor, total, mean, max, min, stddev, cv, zd, active, entropy, elements] = (*ts)[i];
385423
tensor = name_as_vec.data();
386-
bias = total;
387-
count = nval;
424+
total = total_bias;
425+
mean = mean_bias;
426+
max = max_bias;
427+
min = min_bias;
428+
stddev = dev;
429+
cv = rmsd;
430+
active = active_ratio;
431+
entropy = ent;
432+
elements = static_cast<int>(activations.size());
433+
zd = static_cast<float>(z_score) / static_cast<float>(elements);
388434
}
389435
}
390436
return true;
@@ -633,42 +679,35 @@ int main(int argc, char ** argv) {
633679
return 1;
634680
}
635681

636-
std::vector<Tally> tallies;
682+
std::vector<tensor_statistics> ts;
637683

638684
if (params.show_statistics) {
639685
if (params.in_files.empty() || params.in_files.size() > 1) {
640686
LOG_ERR("\nError: a single imatrix file is required to compute tensor statistics\n\n");
641687
return 1;
642688
}
643-
if (!g_collector.load_imatrix(params.in_files[0].c_str(), & tallies)) {
689+
if (!g_collector.load_imatrix(params.in_files[0].c_str(), & ts)) {
644690
LOG_ERR("\nError: %s is not a valid imatrix file\n\n", params.in_files[0].c_str());
645691
return 1;
646692
}
647-
if (tallies.empty()) {
693+
if (ts.empty()) {
648694
LOG_ERR("Error: cannot compute statistics for %s\n\n", params.in_files[0].c_str());
649695
return 1;
650696
}
651-
double total = 0;
652-
for (const auto & tallie : tallies) {
653-
total += tallie.bias;
654-
}
655697

656-
struct tally_sort {
657-
bool operator()(const Tally& x, const Tally & y) const {
658-
return x.bias > y.bias;
659-
}
660-
};
661-
std::sort(tallies.begin(), tallies.end(), tally_sort());
662-
663-
LOG_INF("\nComputing statistics for %s (%d tensors)\n", params.in_files[0].c_str(), static_cast<int>(tallies.size()));
664-
LOG_INF("\n Layer\t Tensor\t Total Bias\tAvg Bias\t Contribution\n");
665-
LOG_INF("===============================================================================================\n");
666-
for (const auto & [tensor, bias, count] : tallies) {
698+
std::sort(ts.begin(), ts.end(), [](const tensor_statistics &a, const tensor_statistics &b) { return a.total > b.total; });
699+
LOG_INF("\nComputing statistics for %s (%d tensors)\n", params.in_files[0].c_str(), static_cast<int>(ts.size()));
700+
LOG_INF("\n%5s\t%-20s\t%10s\t%7s\t%12s\t%9s\t%10s\t%9s\t%6s\t%12s\t%7s\t%10s\n",
701+
"Layer", "Tensor", "Σ(Bias)", "Min", "Max", "μ", "σ", "% Active", "N", "Entropy", "E (norm)", "ZD Score");
702+
LOG_INF("==========================================================================================================================================================================\n");
703+
for (const auto & [tensor, total, mean, max, min, stddev, cv, zd, active, entropy, elements] : ts) {
667704
std::string layer, name;
668705
process_tensor_name(tensor, layer, name);
669-
LOG_INF("%5s\t%30s\t%15.2f\t%15.4f\t%19.4f%%\n", layer.c_str(), name.c_str(), bias, bias / count, 100.0 * bias / total);
706+
LOG_INF("%5s\t%-20s\t%10.2f\t%7.4f\t%12.4f\t%8.4f\t%9.4f\t%8.2f%%\t%6d\t%12.4f\t%7.2f%%\t%10.4f\n",
707+
layer.c_str(), name.c_str(), total, min, max, mean, stddev, active * 100.0f, elements, entropy, 100.0f * (entropy / std::log2(elements)), 1000.0f * zd);
670708
}
671709
LOG_INF("\n");
710+
672711
return 0;
673712
}
674713

0 commit comments

Comments
 (0)