Team Ai
Apppublic

IDKHowToCodeFr/tinyml-backend

sourceHugging Faceupdated 7h agoView on Hugging Face
1likes
export.py329 linesDownload Raw Back to backend
1import m2cgen as m2c2import numpy as np3 4def generate_c_code(eng, model_name: str, quantize: bool):5    if not eng or model_name not in eng.models:6        return {"error": f"Model {model_name} not found"}7        8    model = eng.models[model_name]9    10    # m2cgen handles Random Forest beautifully, but outputs FP32/double rules11    if model_name == "rf":12        try:13            code = m2c.export_to_c(model)14            if quantize:15                code = "/* WARNING: M2CGen generated FP32 output. INT8 Quantization is not supported directly for Random Forest trees. */\n" + code16            return {"code": code}17        except Exception:18            pass19            20    # For LogReg, use m2cgen if FP32, otherwise manual generation for INT821    if not quantize and model_name == "logreg":22        try:23            code = m2c.export_to_c(model)24            return {"code": code}25        except Exception:26            pass27    28    # Manual C-code generation for all model types29    try:30        L = []31        L.append("/* ====================================================== */")32        L.append(f"/* TinyML C Export: {model_name}                           */")33        q_text = "INT8 Quantized" if quantize else "FP32 Double"34        L.append(f"/* Auto-generated for ARM Cortex-M / ESP32 ({q_text}) */")35        L.append("/* ====================================================== */")36        L.append("")37        L.append("#include <math.h>")38        L.append("#include <stdint.h>")39        L.append("#include <string.h>")40        L.append("")41        42        if model_name == "svm" and hasattr(model, 'coef_'):43            coefs = model.coef_44            intercepts = model.intercept_45            n_classes = len(model.classes_)46            n_features = coefs.shape[1]47            L.append(f"/* Linear SVM with {n_classes} classes, {n_features} features */")48            L.append(f"#define N_FEATURES {n_features}")49            L.append(f"#define N_CLASSES {n_classes}")50            L.append(f"#define N_HYPERPLANES {coefs.shape[0]}")51            L.append("")52 53            if quantize:54                scale_factor = 127.0 / max(np.max(np.abs(coefs)), np.max(np.abs(intercepts)), 1e-6)55                L.append(f"/* Quantization Scale: {scale_factor:.4f} */")56                L.append("static const int8_t SVM_COEF[N_HYPERPLANES][N_FEATURES] = {")57                for row in coefs:58                    vals = ", ".join([str(int(round(v * scale_factor))) for v in row])59                    L.append(f"    {{{vals}}},")60                L.append("};")61                L.append("")62                vals = ", ".join([str(int(round(v * scale_factor))) for v in intercepts])63                L.append(f"static const int8_t SVM_INTERCEPT[N_HYPERPLANES] = {{{vals}}};")64                L.append("")65                L.append("int predict(int8_t *features) {")66                L.append("    int32_t scores[N_CLASSES] = {0};")67                L.append("    int h = 0;")68                L.append("    for (int i = 0; i < N_CLASSES; i++) {")69                L.append("        for (int j = i + 1; j < N_CLASSES; j++) {")70                L.append("            int32_t decision = SVM_INTERCEPT[h];")71                L.append("            for (int f = 0; f < N_FEATURES; f++) {")72                L.append("                decision += (int32_t)SVM_COEF[h][f] * features[f];")73                L.append("            }")74                L.append("            if (decision > 0) scores[i] += 1;")75                L.append("            else scores[j] += 1;")76                L.append("            h++;")77                L.append("        }")78                L.append("    }")79                L.append("    int best = 0;")80                L.append("    for (int c = 1; c < N_CLASSES; c++) {")81                L.append("        if (scores[c] > scores[best]) best = c;")82                L.append("    }")83                L.append("    return best;")84                L.append("}")85            else:86                L.append("static const double SVM_COEF[N_HYPERPLANES][N_FEATURES] = {")87                for row in coefs:88                    vals = ", ".join([f"{v:.6f}" for v in row])89                    L.append(f"    {{{vals}}},")90                L.append("};")91                L.append("")92                vals = ", ".join([f"{v:.6f}" for v in intercepts])93                L.append(f"static const double SVM_INTERCEPT[N_HYPERPLANES] = {{{vals}}};")94                L.append("")95                L.append("int predict(double *features) {")96                L.append("    double scores[N_CLASSES] = {0};")97                L.append("    int h = 0;")98                L.append("    for (int i = 0; i < N_CLASSES; i++) {")99                L.append("        for (int j = i + 1; j < N_CLASSES; j++) {")100                L.append("            double decision = SVM_INTERCEPT[h];")101                L.append("            for (int f = 0; f < N_FEATURES; f++) {")102                L.append("                decision += SVM_COEF[h][f] * features[f];")103                L.append("            }")104                L.append("            if (decision > 0) scores[i] += 1.0;")105                L.append("            else scores[j] += 1.0;")106                L.append("            h++;")107                L.append("        }")108                L.append("    }")109                L.append("    int best = 0;")110                L.append("    for (int c = 1; c < N_CLASSES; c++) {")111                L.append("        if (scores[c] > scores[best]) best = c;")112                L.append("    }")113                L.append("    return best;")114                L.append("}")115 116        elif model_name == "logreg" and hasattr(model, 'coef_'):117            coefs = model.coef_118            intercepts = model.intercept_119            n_classes = coefs.shape[0] if len(model.classes_) > 2 else 2120            n_features = coefs.shape[1]121            L.append(f"/* Logistic Regression with {n_classes} classes, {n_features} features */")122            L.append(f"#define N_FEATURES {n_features}")123            L.append(f"#define N_CLASSES {coefs.shape[0]}")124            L.append("")125            126            if quantize:127                scale_factor = 127.0 / max(np.max(np.abs(coefs)), np.max(np.abs(intercepts)), 1e-6)128                L.append(f"/* Quantization Scale: {scale_factor:.4f} */")129                L.append("static const int8_t LOGREG_COEF[N_CLASSES][N_FEATURES] = {")130                for row in coefs:131                    vals = ", ".join([str(int(round(v * scale_factor))) for v in row])132                    L.append(f"    {{{vals}}},")133                L.append("};")134                L.append("")135                vals = ", ".join([str(int(round(v * scale_factor))) for v in intercepts])136                L.append(f"static const int8_t LOGREG_INTERCEPT[N_CLASSES] = {{{vals}}};")137                L.append("")138                L.append("int predict(int8_t *features) {")139                L.append("    int32_t scores[N_CLASSES];")140                L.append("    for (int c = 0; c < N_CLASSES; c++) {")141                L.append(f"        scores[c] = LOGREG_INTERCEPT[c] * {int(scale_factor)};")142                L.append("        for (int f = 0; f < N_FEATURES; f++) {")143                L.append("            scores[c] += (int32_t)LOGREG_COEF[c][f] * features[f];")144                L.append("        }")145                L.append("    }")146                L.append("    int best = 0;")147                L.append("    for (int c = 1; c < N_CLASSES; c++) {")148                L.append("        if (scores[c] > scores[best]) best = c;")149                L.append("    }")150                L.append("    return best;")151                L.append("}")152            else:153                L.append("static const double LOGREG_COEF[N_CLASSES][N_FEATURES] = {")154                for row in coefs:155                    vals = ", ".join([f"{v:.6f}" for v in row])156                    L.append(f"    {{{vals}}},")157                L.append("};")158                L.append("")159                vals = ", ".join([f"{v:.6f}" for v in intercepts])160                L.append(f"static const double LOGREG_INTERCEPT[N_CLASSES] = {{{vals}}};")161                L.append("")162                L.append("int predict(double *features) {")163                L.append("    double scores[N_CLASSES];")164                L.append("    for (int c = 0; c < N_CLASSES; c++) {")165                L.append("        scores[c] = LOGREG_INTERCEPT[c];")166                L.append("        for (int f = 0; f < N_FEATURES; f++) {")167                L.append("            scores[c] += LOGREG_COEF[c][f] * features[f];")168                L.append("        }")169                L.append("    }")170                L.append("    int best = 0;")171                L.append("    for (int c = 1; c < N_CLASSES; c++) {")172                L.append("        if (scores[c] > scores[best]) best = c;")173                L.append("    }")174                L.append("    return best;")175                L.append("}")176 177        elif model_name == "small_nn" and hasattr(model, 'coefs_'):178            layers = model.coefs_179            biases = model.intercepts_180            arch = " -> ".join([str(l.shape[0]) for l in layers] + [str(layers[-1].shape[1])])181            L.append(f"/* MLP Neural Network: {len(layers)} layers */")182            L.append(f"/* Architecture: {arch} */")183            L.append("")184            185            for idx, (W, b) in enumerate(zip(layers, biases)):186                n_in, n_out = W.shape187                L.append(f"#define L{idx}_IN {n_in}")188                L.append(f"#define L{idx}_OUT {n_out}")189                L.append(f"static const double W{idx}[{n_in}][{n_out}] = {{")190                for row in W:191                    vals = ", ".join([f"{v:.6f}" for v in row])192                    L.append(f"    {{{vals}}},")193                L.append("};")194                bvals = ", ".join([f"{v:.6f}" for v in b])195                L.append(f"static const double B{idx}[{n_out}] = {{{bvals}}};")196                L.append("")197            198            L.append("static inline double relu(double x) { return x > 0 ? x : 0; }")199            L.append("")200 201            if quantize:202                # Calculate global max for int8 scaling203                max_val = max([np.max(np.abs(w)) for w in layers] + [np.max(np.abs(b)) for b in biases] + [1e-6])204                scale_factor = 127.0 / max_val205                L.append(f"/* INT8 Quantization Scale Factor: {scale_factor:.4f} */")206                for idx, (W, b) in enumerate(zip(layers, biases)):207                    n_in, n_out = W.shape208                    L.append(f"static const int8_t W{idx}[{n_in}][{n_out}] = {{")209                    for row in W:210                        vals = ", ".join([str(int(round(v * scale_factor))) for v in row])211                        L.append(f"    {{{vals}}},")212                    L.append("};")213                    bvals = ", ".join([str(int(round(v * scale_factor))) for v in b])214                    L.append(f"static const int8_t B{idx}[{n_out}] = {{{bvals}}};")215                    L.append("")216                L.append("static inline int32_t relu_int(int32_t x) { return x > 0 ? x : 0; }")217                L.append("")218                L.append("int predict(int8_t *input) {")219                for idx in range(len(layers)):220                    n_in = layers[idx].shape[0]221                    n_out = layers[idx].shape[1]222                    is_last = (idx == len(layers) - 1)223                    src = "input" if idx == 0 else f"a{idx-1}"224                    L.append(f"    int32_t a{idx}[{n_out}];")225                    L.append(f"    for (int j = 0; j < {n_out}; j++) {{")226                    L.append(f"        a{idx}[j] = B{idx}[j] * {int(scale_factor)}; /* scale bias */")227                    L.append(f"        for (int i = 0; i < {n_in}; i++) {{")228                    L.append(f"            a{idx}[j] += (int32_t){src}[i] * W{idx}[i][j];")229                    L.append(f"        }}")230                    if not is_last:231                        L.append(f"        a{idx}[j] = relu_int(a{idx}[j]) / {int(scale_factor)}; /* rescale */")232                    L.append(f"    }}")233                last_idx = len(layers) - 1234                last_out = layers[-1].shape[1]235                L.append(f"    int best = 0;")236                L.append(f"    for (int c = 1; c < {last_out}; c++) {{")237                L.append(f"        if (a{last_idx}[c] > a{last_idx}[best]) best = c;")238                L.append(f"    }}")239                L.append(f"    return best;")240                L.append("}")241            else:242                for idx, (W, b) in enumerate(zip(layers, biases)):243                    n_in, n_out = W.shape244                    L.append(f"#define L{idx}_IN {n_in}")245                    L.append(f"#define L{idx}_OUT {n_out}")246                    L.append(f"static const double W{idx}[{n_in}][{n_out}] = {{")247                    for row in W:248                        vals = ", ".join([f"{v:.6f}" for v in row])249                        L.append(f"    {{{vals}}},")250                    L.append("};")251                    bvals = ", ".join([f"{v:.6f}" for v in b])252                    L.append(f"static const double B{idx}[{n_out}] = {{{bvals}}};")253                    L.append("")254 255            L.append("static inline double relu(double x) { return x > 0 ? x : 0; }")256            L.append("")257            L.append("int predict(double *input) {")258            for idx in range(len(layers)):259                n_in = layers[idx].shape[0]260                n_out = layers[idx].shape[1]261                is_last = (idx == len(layers) - 1)262                src = "input" if idx == 0 else f"a{idx-1}"263                L.append(f"    double a{idx}[{n_out}];")264                L.append(f"    for (int j = 0; j < {n_out}; j++) {{")265                L.append(f"        a{idx}[j] = B{idx}[j];")266                L.append(f"        for (int i = 0; i < {n_in}; i++) {{")267                L.append(f"            a{idx}[j] += {src}[i] * W{idx}[i][j];")268                L.append(f"        }}")269                if not is_last:270                    L.append(f"        a{idx}[j] = relu(a{idx}[j]);")271                L.append(f"    }}")272            273            last_idx = len(layers) - 1274            last_out = layers[-1].shape[1]275            L.append(f"    int best = 0;")276            L.append(f"    for (int c = 1; c < {last_out}; c++) {{")277            L.append(f"        if (a{last_idx}[c] > a{last_idx}[best]) best = c;")278            L.append(f"    }}")279            L.append(f"    return best;")280            L.append("}")281            282        elif model_name == "knn" and hasattr(model, '_fit_X'):283            n_samples = min(model._fit_X.shape[0], 100)284            n_feats = model._fit_X.shape[1]285            L.append(f"/* KNN Lookup Table: {n_samples} reference samples */")286            L.append(f"#define N_NEIGHBORS {model.n_neighbors}")287            L.append(f"#define N_SAMPLES {n_samples}")288            L.append(f"#define N_FEATURES {n_feats}")289            L.append("")290            L.append("static const double REF[N_SAMPLES][N_FEATURES] = {")291            for row in model._fit_X[:n_samples]:292                vals = ", ".join([f"{v:.4f}" for v in row])293                L.append(f"    {{{vals}}},")294            L.append("};")295            L.append("")296            labels_str = ", ".join([str(int(l)) for l in model._y[:n_samples]])297            L.append(f"static const int LABELS[N_SAMPLES] = {{{labels_str}}};")298            L.append("")299            L.append("int predict(double *features) {")300            L.append("    double dists[N_SAMPLES];")301            L.append("    for (int i = 0; i < N_SAMPLES; i++) {")302            L.append("        dists[i] = 0.0;")303            L.append("        for (int f = 0; f < N_FEATURES; f++) {")304            L.append("            double d = features[f] - REF[i][f];")305            L.append("            dists[i] += d * d;")306            L.append("        }")307            L.append("    }")308            L.append("    int votes[10] = {0};")309            L.append("    for (int k = 0; k < N_NEIGHBORS; k++) {")310            L.append("        int mi = 0;")311            L.append("        for (int i = 1; i < N_SAMPLES; i++) {")312            L.append("            if (dists[i] < dists[mi]) mi = i;")313            L.append("        }")314            L.append("        votes[LABELS[mi]]++;")315            L.append("        dists[mi] = 1e18;")316            L.append("    }")317            L.append("    int best = 0;")318            L.append("    for (int i = 1; i < 10; i++) {")319            L.append("        if (votes[i] > votes[best]) best = i;")320            L.append("    }")321            L.append("    return best;")322            L.append("}")323        else:324            return {"error": f"Model {model_name} cannot be exported to C."}325        326        return {"code": "\n".join(L)}327    except Exception as e:328        return {"error": f"Export failed: {str(e)}"}329