Rockchip · AI & Linux Hub

KI-Inferenz

RKNN2 SDK V2.5.0 · Modelle mit dynamischen Formen und Bereitstellung

RKNN2 SDK V2.5.0 · Modelle mit dynamischen Formen und Bereitstellung · Vollständige Entwicklerhandbuchreihe

· ArmSoM Editorial · Rockchip source documentation · 13 Min. Lesezeit

Plattformreferenz nach dem Rockchip RKNN2 SDK Development Guide V2.5.0. Erfasst werden nur die im Original genannten SoCs sowie Linux-/Android-Runtime- und Toolkit-Kombinationen. Funktionen gelten unter den jeweiligen Chip-, SDK-, Treiber- und Kernelbedingungen und nicht automatisch für jedes ArmSoM-Board. Prüfen Sie vor der Bereitstellung BSP, NPU-Treiber, Runtime und Modellversion des konkreten Boards. Es erfolgte keine ArmSoM-Hardwarevalidierung; Protokolle, Leistungswerte und Beispiele stammen aus dem Original.

Quellen und Prüfung: Chinesisch folgt der chinesischen V2.5.0 (182 Seiten); Englisch, Russisch und Deutsch folgen der englischen V2.5.0 (208 Seiten). Quellenspezifische technische Angaben bleiben erhalten; Inhalte älterer Versionen werden nicht als neue Fakten dargestellt. Chinesische Abbildungen stammen aus dem chinesischen PDF, EN/RU/DE-Abbildungen aus dem englischen PDF einschließlich separat extrahierter Vektorflussdiagramme. Abschnitts- und Formelnummern bleiben erhalten. Einige Querverweise verwenden alte Kapitelnummern; Dynamic-Shape-Beispiele enthalten „1224+140=264“, TP_NORM-Aussagen widersprechen sich, und init()/rknn.val_perf() weichen von späteren API-Beschreibungen ab. Diese Quellenprobleme bleiben dokumentiert und sind vor Ausführung anhand der tatsächlichen V2.5.0-Header und API-Referenzen zu prüfen. Code und Parameter bleiben erhalten; PDF-Umbrüche und Tabellenfortsetzungen wurden bereinigt.

Rockchip RKNN2 SDK V2.5.0

RKNN2 SDK V2.5.0 · 13/22

1. Softwarestack und Grundbegriffe

2. Entwicklungsumgebung, Versionen und Board-Anbindung

3. Entwicklungsablauf und Schnittstellenwahl

4. Modellkonvertierung und ONNX-Bearbeitung

5. Quantisierung, hybride Quantisierung und QAT

6. Inferenz sowie Genauigkeits-, Leistungs- und Speicherbewertung

7. Runtime-Lebenszyklus, Layouts und Speicher

8. Zero-Copy-Schnittstellen und Ein-/Ausgabebedingungen

9. C-API-Bereitstellung und Codegenerierung

10. Python-Bereitstellung mit Toolkit Lite2

11. Matmul-API und Matrixlayouts

12. NPU-Mehrkernkonfiguration und Leistung

Modelle mit dynamischen Formen und Bereitstellung

14. Inferenz mit mehreren Batches

15. NPU-SRAM auf RK3588/RK3576

16. Cache-Kohärenz und Sparse-Inferenz

17. Benutzerdefinierte Operatoren: Python und C API

18. Bereitstellung verschlüsselter Modelle

19. Diagnose von Genauigkeitsproblemen

20. Leistungsanalyse und Netzwerkoptimierung

21. Speicheranalyse, gemeinsame Nutzung und Wiederverwendung

22. FAQ, Fehlerbehandlung und Referenzen

4.1.3 Dynamische Form

4.1.3.1 Einführung in dynamische Formen

Dynamische Formen erlauben Änderungen der Eingabeform zur Laufzeit und erhöhen die Flexibilität bei variablen Datengrößen. Als RKNN nur statische Formen unterstützte, waren für mehrere Eingabeformen separate Modelle und Inferenzkontexte erforderlich. Mit dynamischen Formen genügt ein RKNN-Modell ähnlicher Größe wie das statische sowie ein Kontext; dies spart Flash und DDR. Das ist insbesondere für Bildverarbeitung und Sequenzmodelle wichtig. Typische Anwendungen:

Variable Sequenzlängen, etwa in NLP-Modellen wie BERT und GPT.

Variable räumliche Dimensionen, etwa bei Segmentierung und Stilübertragung.

Variable Batch-Dimensionen in Batch-Modellen.

Objekterkennungsmodelle mit variabler Ausgabeanzahl.

4.1.3.2 Anforderungen an RKNN-SDK-Version und Plattform

RKNN-Toolkit2-Version >=1.5.0

Version der RKNPU-Runtime-Bibliothek (librknnrt.so) >=1.5.0

Die NPU der Plattformen RK3562/RK3566/RK3568/RK3576/RK3588/RK3588S/RV1126B unterstützt diese Funktion.

4.1.3.3 RKNN-Modell mit dynamischer Form erzeugen

Erstellen eines RKNN-Modells mit dynamischer Form über die Python-API von RKNN-Toolkit2:

1 Prüfen, ob das Modell dynamische Formen unterstützt.

RKNN-Toolkit2 kann auch ein ursprünglich statisches Modell zu dynamischen Formen erweitern. Zuerst prüfen, dass keine Operatoren oder Teilgraphen die Formänderung beschränken: Die Form einer Konstante lässt sich etwa nicht ändern und führt zu einem Konvertierungsfehler. Bei nicht unterstützter Erweiterung die Struktur entsprechend der Fehlermeldung ändern und das Modell neu trainieren. Ein von Anfang an dynamisches Modell wird empfohlen.

  1. Verwendete Eingabeformen festlegen

Wegen der NPU-Eigenschaften sind keine beliebigen Formänderungen möglich; eine begrenzte Auswahl ist vorzugeben. Bei mehreren Eingängen muss die Formanzahl je Eingang übereinstimmen. Python-Beispiel einer Caffe-Konvertierung:

# Python
dynamic_input = [
    [[1,3,224,224]], # set the first shape for all inputs
    [[1,3,192,192]], # set the second shape for all inputs
    [[1,3,160,160]], # set the third shape for all inputs
]

# Pre-process config
rknn.config(mean_values=[103.94, 116.78, 123.68], std_values=[58.82, 58.82, 58.82],
quant_img_RGB2BGR=True, dynamic_input=dynamic_shapes)

Diese Konfiguration erzeugt ein Modell mit drei Formen: [1,3,224,224], [1,3,192,192], [1,3,160,160].

dynamic_input folgt dem Layout des ursprünglichen Frameworks. Bei RGB-Bildern 224x224 verwendet TensorFlow/TFLite beispielsweise [1,224,224,3], ONNX [1,3,224,224].

1 Quantisierung

Nach dem Setzen der Formen bei Bedarf den Quantisierungskalibrierungsdatensatz angeben. Verwendet wird die maximale Auflösung, also die Formgruppe mit der größten Summe aller Eingabegrößen. Beispiel: Eingang 1 hat [1,224]/[1,112], Eingang 2 [1,40]/[1,80]. Die Gruppensummen sind 1224+140=264 und 1112+180=192 (so im PDF gedruckt; Multiplikationszeichen fehlen im Original, die Schreibweise ist zu prüfen). Daher wird mit [1,224] und [1,40] quantisiert.

Bei jpg/png sind verschiedene Auflösungen möglich: Vor der Quantisierung skaliert das Werkzeug mit OpenCV resize auf die maximale Auflösung.

Bei npy muss die maximale Eingabeform verwendet werden. Danach verwenden alle Formen zur Laufzeit denselben Quantisierungsparametersatz.

Die maximale Eingabeform wird auch bei rknn.config ausgegeben:

W config: The 'dynamic_input' function has been enabled, the MaxShape is dynamic_input[0] = [[1,224],[1,40]]!
The following functions are subject to the MaxShape:
1. The quantified dataset needs to be configured according to MaxShape
2. The eval_perf or eval_memory return the results of MaxShape
  1. Inferenzbewertung oder Genauigkeitsanalyse

Für Inferenz oder Genauigkeitsanalyse Eingaben einer der in Schritt 2 gesetzten Formgruppen liefern. Die Schnittstellen entsprechen dem statischen Modellfall und werden hier nicht erneut erläutert.

Vollständiges Beispiel für dynamische Formen: https://github.com/airockchip/rknn-toolkit2/tree/master/rknn-toolkit2/examples/functions/dynamic_shape

4.1.3.4 Bereitstellung über die C-API

Stellen Sie das erzeugte Modell mit dynamischer Form über die RKNPU2-C-API bereit. Je nach Schnittstelle erfolgt dies über die allgemeine API oder Zero-Copy-API.

4.1.3.4.1 Allgemeine API

Bereitstellung eines dynamischen RKNN-Modells über die allgemeine API:

Abbildung 4-5. Allgemeiner API-Ablauf für dynamische Eingabeformen

Abbildung 4-5. Allgemeiner API-Ablauf für dynamische Eingabeformen

Nach dem Laden eines dynamischen RKNN-Modells kann die Form zur Laufzeit geändert werden. Zuerst unterstützte Formen mit rknn_query() abfragen. Die Liste jedes Eingangs wird als rknn_input_range mit Name, Layout, Formanzahl und konkreten Formen zurückgegeben. Anschließend den Zeiger auf ein rknn_tensor_attr-Array mit den Eingabeformen an rknn_set_input_shapes() übergeben. Nach erfolgreichem Setzen mit rknn_query() die aktuellen Ein-/Ausgabeformen erneut abfragen.

Die Inferenz gemäß allgemeiner API abschließen. Bei jedem Formwechsel die neue Form setzen, passende Daten vorbereiten und rknn_inputs_set() erneut aufrufen. Ohne Formwechsel ist kein erneutes rknn_set_input_shapes() erforderlich.

1 Initialisierung

Initialisieren Sie das dynamische RKNN-Modell mit rknn_init().

Einschränkungen bei der Kontextinitialisierung dynamischer Modelle:

Gemeinsame Gewichte werden nicht unterstützt: keine Initialisierung mit RKNN_FLAG_SHARE_WEIGHT_MEM.

Kontextwiederverwendung wird nicht unterstützt; siehe rknn_dup_context.

2. Unterstützte Eingabeformkombinationen abfragen

Nach der Initialisierung liefert rknn_query() die Formen jedes Eingangs in rknn_input_range mit Name, Layout, Anzahl und konkreten Formen. C-Beispiel:

// Query the input shapes supported by the model
rknn_input_range dyn_range[io_num.n_input];
memset(dyn_range, 0, io_num.n_input * sizeof(rknn_input_range));
for (uint32_t i = 0; i < io_num.n_input; i++)
{
    dyn_range[i].index = i;
    ret = rknn_query(ctx, RKNN_QUERY_INPUT_DYNAMIC_RANGE, &dyn_range[i], sizeof(rknn_input_range));
    if (ret != RKNN_SUCC)
    {
        fprintf(stderr, "rknn_query error! ret=%d\n", ret);
        return -1;
    }
    dump_input_dynamic_range(&dyn_range[i]);
}

Hinweis: Formen mehrerer Eingänge entsprechen einander nach Reihenfolge, ohne Kreuzkombinationen. Die erste Form des ersten Eingangs passt etwa nur zur ersten Form des zweiten. Für A=[1,224]/[1,112] und B=[1,40]/[1,80] werden nur folgende zwei Gruppen unterstützt:

Form A = [1,224], Form B=[1,40]

Form A = [1,112], Form B=[1,80]

3 Eingabeform setzen

Beim ersten Setzen oder Ändern der Eingabeform rknn_set_input_shapes() aufrufen. Nach Laden eines dynamischen Modells kann die Form zur Laufzeit geändert werden. Im übergebenen rknn_tensor_attr-Array aller Eingänge beschreiben dims, n_dims und fmt jedes Elements die aktuelle Form. C-Beispiel:

/**
 dynamic inputs shape range:
 index=0, name=data, shape_number=2, range=[[1, 224, 224, 3],[1, 112, 224, 3]], fmt = NHWC
**/
input_attrs[0].dims[0] = 1;
input_attrs[0].dims[1] = 224;
input_attrs[0].dims[2] = 224;
input_attrs[0].dims[3] = 3;
input_attrs[0].fmt=RKNN_TENSOR_NHWC;
ret = rknn_set_input_shapes(ctx, io_num.n_input, input_attrs);
if (ret < 0) {
  fprintf(stderr, "rknn_set_input_shapes error! ret=%d\n", ret);
  return -1;
}

io_num.n_input ist die Eingabeanzahl, input_attrs das rknn_tensor_attr-Array der Modelleingaben.

Hinweis: Die gesetzte Form muss in der in Schritt 2 abgefragten Liste enthalten sein.

Nach erfolgreichem Setzen die Ein-/Ausgabeformen erneut mit rknn_query abfragen. C-Beispiel:

// Get the input and output shapes of the current inference
rknn_tensor_attr cur_input_attrs[io_num.n_input];
memset(cur_input_attrs, 0, io_num.n_input * sizeof(rknn_tensor_attr));
for (uint32_t i = 0; i < io_num.n_input; i++)
{
    cur_input_attrs[i].index = i;
    ret = rknn_query(ctx, RKNN_QUERY_CURRENT_INPUT_ATTR, &(cur_input_attrs[i]), sizeof(rknn_tensor_attr));
    if (ret < 0)
    {
        printf("rknn_init error! ret=%d\n", ret);
        return -1;
    }
    dump_tensor_attr(&cur_input_attrs[i]);
}
rknn_tensor_attr cur_output_attrs[io_num.n_output];
memset(cur_output_attrs, 0, io_num.n_output * sizeof(rknn_tensor_attr));
for (uint32_t i = 0; i < io_num.n_output; i++)
{
    cur_output_attrs[i].index = i;
    ret = rknn_query(ctx, RKNN_QUERY_CURRENT_OUTPUT_ATTR, &(cur_output_attrs[i]), sizeof(rknn_tensor_attr));
    if (ret != RKNN_SUCC)
    {
        printf("rknn_query fail! ret=%d\n", ret);
        return -1;
    }
    dump_tensor_attr(&cur_output_attrs[i]);
}

Hinweise:

rknn_set_input_shape verlangt fmt=NHWC bei vierdimensionaler Eingabe, andernfalls UNDEFINED.

Vor rknn_set_input_shapes sind Formabfragen mit Präfix RKNN_QUERY_CURRENT ungültig.

4 Inferenz

Nach dem Setzen der aktuellen Form seien die Eingabetensorinformationen in cur_input_attrs gespeichert. C-Beispiel für die allgemeine API:

// Set input information
rknn_input inputs[io_num.n_input];
memset(inputs, 0, io_num.n_input * sizeof(rknn_input));
for (int i = 0; i < io_num.n_input; i++)
{
    int height = cur_input_attrs[i].fmt == RKNN_TENSOR_NHWC ? cur_input_attrs[i].dims[1] : cur_input_attrs[i].dims[2];
    int width = cur_input_attrs[i].fmt == RKNN_TENSOR_NHWC ? cur_input_attrs[i].dims[2] : cur_input_attrs[i].dims[3];
    cv::resize(imgs[i], imgs[i], cv::Size(width, height));
    inputs[i].index = i;
    inputs[i].pass_through = 0;
    inputs[i].type = RKNN_TENSOR_UINT8;
    inputs[i].fmt = RKNN_TENSOR_NHWC;
    inputs[i].buf = imgs[i].data;
    inputs[i].size = imgs[i].total() * imgs[i].channels();
}

// Convert the input data into the correct format and put it into the input buffer
ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
if (ret < 0)
{
    printf("rknn_input_set fail! ret=%d\n", ret);
    return -1;
}

// Run inference
printf("Begin perf ...\n");
double total_time = 0;
for (int i = 0; i < loop_count; ++i)
{
    int64_t start_us = getCurrentTimeUs();
    ret = rknn_run(ctx, NULL);
    int64_t elapse_us = getCurrentTimeUs() - start_us;
    if (ret < 0)
    {
        printf("rknn run error %d\n", ret);
        return -1;
    }
    total_time += elapse_us / 1000.f;
    printf("%4d: Elapse Time = %.2fms, FPS = %.2f\n", i, elapse_us / 1000.f, 1000.f
* 1000.f / elapse_us);
}
printf("Avg FPS = %.3f\n", loop_count * 1000.f / total_time);

// Get output results
rknn_output outputs[io_num.n_output];
memset(outputs, 0, io_num.n_output * sizeof(rknn_output));
for (uint32_t i = 0; i < io_num.n_output; ++i)
{
    outputs[i].want_float = 1;
    outputs[i].index = i;
    outputs[i].is_prealloc = 0;
}

ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL);
if (ret < 0)
{
    printf("rknn_outputs_get fail! ret=%d\n", ret);
    return ret;
}

// Release the output buffer
ret = rknn_outputs_release(ctx, io_num.n_output, outputs);
4.1.3.4.2 Zero-Copy-API

Bei Zero-Copy nach der Initialisierung Formen mit rknn_query() abfragen und Speicher mit rknn_create_mem() zuweisen. Ein rknn_tensor_attr-Array aller Eingabeformen an rknn_set_input_shapes() übergeben, anschließend die gesetzten Formen erneut mit rknn_query() abfragen. Schließlich den Speicher mit rknn_set_io_mem() setzen. Bei jedem Formwechsel Form erneut setzen, Daten passender Größe vorbereiten und rknn_set_io_mem() wieder aufrufen. Ohne Formwechsel ist kein erneutes rknn_set_input_shapes() erforderlich. Typischer Ablauf:

Technische Originalabbildung

Abbildung 4-6. Zero-Copy-API-Ablauf bei dynamischer Eingabeform

Initialisierung, Abfrage der Formkombinationen und Setzen der Form entsprechen der allgemeinen API oben und werden nicht wiederholt.

Nach dem Setzen der Form werden andere Schnittstellen verwendet. Zero-Copy-C-Beispiel:

// Create the largest input tensor memory
rknn_tensor_mem *input_mems[io_num.n_input];
for (int i = 0; i < io_num.n_input; i++) {
    // default input type is int8 (normalize and quantize need compute in outside)
    // if set uint8, will fuse normalize and quantize to npu
    input_attrs[i].type = RKNN_TENSOR_UINT8;
    // default fmt is NHWC, npu only support NHWC in zero copy mode
    input_attrs[i].fmt = RKNN_TENSOR_NHWC;
    input_mems[i] = rknn_create_mem(ctx, input_attrs[i].size_with_stride);
...
// Create the largest output tensor memory
rknn_tensor_mem *output_mems[io_num.n_output];
for (uint32_t i = 0; i < io_num.n_output; ++i) {
    // default output type is depend on model, this require float32 to compute top5
    // allocate float32 output tensor
    int output_size = output_attrs[i].size * sizeof(float);
    output_mems[i] = rknn_create_mem(ctx, output_size);
...
// Load the input and set the model input shape, which is called once each time the input shape is switched
for (int s = 0; s < shape_num; ++s) {
    for (int i = 0; i < io_num.n_input; i++) {
        for (int j = 0; j < input_attrs[i].n_dims; ++j) {
           input_attrs[i].dims[j] = shape_range[i].dyn_range[s][j];
    .....
    ret = rknn_set_input_shapes(ctx, io_num.n_input, input_attrs);
    if (ret < 0) {
        fprintf(stderr, "rknn_set_input_shape error! ret=%d\n", ret);
        return -1;
    ....
    // Get the input and output shapes of the current inference
    printf("current input tensors:\n");
    rknn_tensor_attr cur_input_attrs[io_num.n_input];
    memset(cur_input_attrs, 0, io_num.n_input * sizeof(rknn_tensor_attr));
    for (uint32_t i = 0; i < io_num.n_input; i++) {
        cur_input_attrs[i].index = i;
        // query info
        ret = rknn_query(ctx, RKNN_QUERY_CURRENT_INPUT_ATTR, &(cur_input_attrs[i]), sizeof(rknn_tensor_attr));
        if (ret < 0) {
           printf("rknn_init error! ret=%d\n", ret);
           return -1;
        dump_tensor_attr(&cur_input_attrs[i]);
        ...
        printf("current output tensors:\n");
    rknn_tensor_attr cur_output_attrs[io_num.n_output];
    memset(cur_output_attrs, 0, io_num.n_output * sizeof(rknn_tensor_attr));
    for (uint32_t i = 0; i < io_num.n_output; i++) {
        cur_output_attrs[i].index = i;
        // query info
        ret = rknn_query(ctx, RKNN_QUERY_CURRENT_OUTPUT_ATTR, &
(cur_output_attrs[i]), sizeof(rknn_tensor_attr));
        if (ret != RKNN_SUCC) {
           printf("rknn_query fail! ret=%d\n", ret);
           return -1;
        ...
        dump_tensor_attr(&cur_output_attrs[i]);
    // Specify the number of NPU cores, only 3588/3576 is supported
    rknn_set_core_mask(ctx, (rknn_core_mask)core_mask);
    // Set input information
    rknn_input inputs[io_num.n_input];
    memset(inputs, 0, io_num.n_input * sizeof(rknn_input));
    std::vector<cv::Mat> resize_imgs;
    resize_imgs.resize(io_num.n_input);
    for (int i = 0; i < io_num.n_input; i++) {
        int height = cur_input_attrs[i].fmt == RKNN_TENSOR_NHWC ? cur_input_attrs[i].dims[1] : cur_input_attrs[i].dims[2];
        int width = cur_input_attrs[i].fmt == RKNN_TENSOR_NHWC ? cur_input_attrs[i].dims[2] : cur_input_attrs[i].dims[3];
        int stride = cur_input_attrs[i].w_stride;
        cv::resize(imgs[i], resize_imgs[i], cv::Size(width, height));
        int input_size = resize_imgs[i].total() * resize_imgs[i].channels();
        // Copy external data to the zero-copy input buffer
        if (width == stride)
           memcpy(input_mems[i]->virt_addr, resize_imgs[i].data, input_size);
        else {
           int height = cur_input_attrs[i].dims[1];
           int channel = cur_input_attrs[i].dims[3];
           // copy from src to dst with stride
           uint8_t *src_ptr = resize_imgs[i].data;
           uint8_t *dst_ptr = (uint8_t *)input_mems[i]->virt_addr;
           // width-channel elements
           int src_wc_elems = width * channel;
           int dst_wc_elems = stride * channel;
           for (int b = 0; b < cur_input_attrs[i].dims[0]; b++) {
               for (int h = 0; h < height; ++h) {
                   memcpy(dst_ptr, src_ptr, src_wc_elems);
                   src_ptr += src_wc_elems;
                   dst_ptr += dst_wc_elems;
           ....
    // Update the input zero-copy buffer memory
    for (int i = 0; i < io_num.n_input; i++) {
        cur_input_attrs[i].type = RKNN_TENSOR_UINT8;
        ret = rknn_set_io_mem(ctx, input_mems[i], &cur_input_attrs[i]);
        if (ret < 0) {
           printf("rknn_set_io_mem fail! ret=%d\n", ret);
           return -1;
     ....
    // Update the output zero-copy buffer memory
    for (uint32_t i = 0; i < io_num.n_output; ++i) {
        // default output type is depend on model, this require float32 to compute top5
        cur_output_attrs[i].type = RKNN_TENSOR_FLOAT32;
        cur_output_attrs[i].fmt = RKNN_TENSOR_NCHW;
        // set output memory and attribute
        ret = rknn_set_io_mem(ctx, output_mems[i], &cur_output_attrs[i]);
        if (ret < 0) {
           printf("rknn_set_io_mem fail! ret=%d\n", ret);
           return -1;
      ....
    // Inference
    printf("Begin perf ...\n");
    double total_time = 0;
    for (int i = 0; i < loop_count; ++i) {
        int64_t start_us = getCurrentTimeUs();
        ret = rknn_run(ctx, NULL);
        int64_t elapse_us = getCurrentTimeUs() - start_us;
        if (ret < 0) {
           printf("rknn run error %d\n", ret);
           return -1;
        ...
        total_time += elapse_us / 1000.f;
        printf("%4d: Elapse Time = %.2fms, FPS = %.2f\n", i, elapse_us / 1000.f, 1000.f * 1000.f / elapse_us);
    printf("Avg FPS = %.3f\n", loop_count * 1000.f / total_time);

Hinweise:

  1. Form und Größe des Eingabepuffers von rknn_set_io_mem() bei dynamischen Formen:

  2. Nach Initialisierung, aber vor rknn_set_input_shapes(), liefern RKNN_QUERY_INPUT_ATTR/RKNN_QUERY_OUTPUT_ATTR über rknn_query() gewöhnlich die größten Formen. Diese Größen können zur Speicherzuweisung dienen. Bei mehreren Eingängen sind einzelne Formen möglicherweise nicht maximal: Den größten unterstützten Bedarf suchen und entsprechend maximalen Ein-/Ausgabespeicher zuweisen.

  3. Für nicht vierdimensionale Eingaben fmt=UNDEFINED und einen Puffer der ursprünglichen Modelleingabeform verwenden; die Größe ergibt sich aus Form und Typ.

  4. Vierdimensionale Eingaben unterstützen fmt=NHWC oder NC1HWC2 mit entsprechendem Puffer und Größe. Form und Größe über die passenden rknn_query-Felder ermitteln.

  5. RKNN_QUERY_CURRENT_INPUT_ATTR/RKNN_QUERY_CURRENT_OUTPUT_ATTR liefern ursprüngliche Modellformen in NHWC oder UNDEFINED. RKNN_QUERY_CURRENT_NATIVE_INPUT_ATTR/RKNN_QUERY_CURRENT_NATIVE_OUTPUT_ATTR liefern für effizientes NPU-Lesen optimierte Formen in NHWC oder NC1HWC2.

  6. Bei NHWC-Puffern in rknn_set_io_mem() shape und fmt von rknn_tensor_attr anhand RKNN_QUERY_CURRENT_INPUT_ATTR setzen; bei NC1HWC2 anhand RKNN_QUERY_CURRENT_NATIVE_INPUT_ATTR.

Vollständiges C-API-Demo für dynamische Formen: https://github.com/airockchip/rknn-toolkit2/tree/master/rknpu2/examples/rknn_dynamic_shape_input_demo

Dieses Thema entdecken → · Lernpfade →