Feature Vector Search

For the Atlas training products , this function is not supported.

For the Atlas A2 training products / Atlas A2 inference products , this function is not supported.

For the Atlas 200I/500 A2 inference products , this function is not supported.

For the Atlas A3 training products / Atlas A3 inference products , this function is not supported.

Principles

Feature vector search is used to verify the feature search function. To do so, you need to generate a random repository in which random features can be searched for. (Currently, the 1:N and M:N search modes are supported. The following sample code uses the 1:N mode as an example.) The process can be broken into the following steps: initialization, adding features to the repository, repository search, precise modification or deletion of the features in the repository, and deinitialization. The API calls are described as follows:

  • Initialization: Call aclInit for initialization. Call aclfvCreateInitPara to create data of the aclfvInitPara type which is used to specify the initialization configuration for feature vector search.
  • Adding features to the repository: Call aclfvCreateFeatureInfo to create data of the aclfvFeatureInfo type as the feature description, and call aclfvRepoAdd to add a repository.
  • Repository search: Call aclfvSearch to search the repository.
  • Precise modification or deletion of the features in the repository: Call aclfvDel and aclfvModify to delete or modify a feature in the repository. The following code uses feature deletion as an example.
  • Deinitialization: Release runtime resources, call aclfvDestroyInitPara to destroy data of the aclfvInitPara type, and call aclfvRelease to deinitialize the feature search module and free the memory.

Sample Code

This section focuses on the code logic of the feature vector search function. For details about initialization and deinitialization, see Initialization and Deinitialization. For details about how to allocate and deallocate runtime resources, see Runtime Resource Allocation and Deallocation.

This part describes the sample code. Click here to view the complete sample code.

After APIs are called, you need to add exception handling branches and record error logs and info logs. The following is a code snippet of key steps only, which is not ready to be built or run.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
//1. Initialization

//2. Allocate runtime resources.
//Set the default run mode to ACL_HOST.
aclrtRunMode runMode = ACL_HOST;

//3. Initialization
//3.1 Initialize the feature search module. The following uses the repository with 100000 features as an example.
size_t fsNum = 100000;
fvInitPara = aclfvCreateInitPara(fsNum);

//3.2 Specify the initialization parameter for feature vector search.
ret = aclfvInit(fvInitPara);

//4. Add the repository and feature vectors.
//4.1 Add the first feature. When creating the feature description, set the offset parameter to 0.
uint32_t offset = 0;
uint32_t featureCount = 1000;
uint32_t featureLen = 36;

//The user-defined function BaseShortFeaAlloc is used to generate random feature data.
void *featureData = BaseShortFeaAlloc(1000, static_cast<size_t>(featureCount), 0);
std::shared_ptr<void> feaBufPtr(featureData, [](void *p){(void)aclrtFreeHost(p);});
void *inputData = featureData;
std::shared_ptr<void> inputDataPtr = nullptr;

//If the run mode is ACL_HOST, allocate memory first, and transfer the random feature data of the host to the device by using the aclrtMemcpy call. Otherwise, directly read the random feature data to the device memory.
if (aclrtGetRunMode(&runMode) == ACL_HOST) {
      //Allocate memory for input data.
      ret = aclrtMalloc(&inputData, featureLen * featureCount, 
                        ACL_MEM_MALLOC_HUGE_FIRST);
      //Read the random feature data to the device memory.
      inputDataPtr.reset(inputData, [](void *p) {(void)aclrtFree(p);});
      //Copy the feature data from the host to the device.
      ret = aclrtMemcpy(inputData,
                        featureLen * featureCount,
                        featureData,
                        featureLen * featureCount,
                        ACL_MEMCPY_HOST_TO_DEVICE);
}

//Create feature description information. inputData indicates the random feature data generated in the previous step.
auto featureInfo = aclfvCreateFeatureInfo(id0, id1, offset, featureLen, featureCount, 
                        reinterpret_cast<uint8_t *>(inputData), featureLen * featureCount);

//Add a repository and add features to the repository. featureInfo indicates the feature description generated in the previous step.
aclError ret = aclfvRepoAdd(SEARCH_1_N, featureInfo);

//Destroy the aclfvFeatureInfo data.
aclfvDestroyFeatureInfo(featureInfo);

//4.2 Add the second feature and precisely delete or modify a feature in the repository. When creating the feature description information, ensure that the offset value is the same as the number of features added to the repository.
offset += featureCount;

//For details about how to add a feature to the repository, see step 4.1.
// ....

uint8_t featureData[36];
for (size_t i = 0; i < 36; i++) {
        featureData[i] = static_cast<uint8_t>(i);
}

//Create memory and transfer the feature data.
void *inputData = nullptr;
aclrtMalloc(&inputData, 36, ACL_MEM_MALLOC_HUGE_FIRST);
std::shared_ptr<void> inputDataPtr(inputData, [](void *p){(void)aclrtFree(p);});
aclrtMemcpyKind kind = ACL_MEMCPY_DEVICE_TO_DEVICE;

//If the run mode is ACL_HOST, copy the feature data to the device. Otherwise, data copy is not required. dataLen indicates the length of the memory allocated using the featureData pointer.
if (aclrtGetRunMode(&runMode) == ACL_HOST) {
        kind = ACL_MEMCPY_HOST_TO_DEVICE;
}
aclrtMemcpy(inputData, 36, featureData, dataLen, kind);

//Create feature description information.
uint32_t id0 = 0;
uint32_t id1 = 0;
auto featureInfo1 = aclfvCreateFeatureInfo(id0, id1, offset, 36, 1,
                                          reinterpret_cast<uint8_t *>(inputData), 36);
std::shared_ptr<aclfvFeatureInfo> featureInfoPtr(featureInfo1,
                                          [](aclfvFeatureInfo *p){(void)aclfvDestroyFeatureInfo(p);});

//Delete a feature.
aclfvDel(featureInfo1);

//4.3 Add features to other repositories. The values for the level-1 and level-2 repositories are both 1.
id0 = 1;
id1 = 1;
offset = 0; 

//For details about how to add a feature to the repository, see step 4.1.
// ....

//5. Repository search (in 1:N mode for example), including feature search preprocessing, 1:N feature search, and feature search result processing.
//5.1 Feature search preprocessing. In 1:N mode, the value of queryCnt must be 1.
uint32_t queryCnt = 1;
uint32_t topK = 5;
uint32_t dataLen = queryCnt * topK * sizeof(uint32_t);
uint32_t resultNumDataLen = queryCnt * sizeof(uint32_t);
const uint32_t tableLen = 32 * 1024; 
uint32_t tableDataLen = queryCnt * tableLen;

//Generate a data table for search and comparison. The user-defined function AdcTabInit is used to initialize the input ADC table for feature search.
uint8_t *tableDataTmp = (uint8_t *)AdcTabInit(1000, queryCnt * 1024);
std::shared_ptr<void> tableDataTmpPtr(tableDataTmp,[](void *p){(void)aclrtFreeHost(p);});

//Allocate memory to the data table. tableDataDev is used to create and search for the input table information.
void *devPtr = nullptr;
aclrtMalloc(&devPtr, tableDataLen, ACL_MEM_MALLOC_HUGE_FIRST);
tableDataDev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});

//Copy the table data to the device.
uint8_t *devPtrTmp = reinterpret_cast<uint8_t *>(devPtr);
for (uint32_t i = 0; i < queryCnt; ++i) {
     for (uint32_t j = 0; j < 32; ++j) {
          uint8_t *dst = devPtrTmp + i * 32 * 1024 + j * 1024;
          uint8_t *src = tableDataTmp + i * 1024;
          aclrtMemcpy(dst, 1024, src, 1024, ACL_MEMCPY_HOST_TO_DEVICE);
     }
}

//Allocate memory for the search result resultNumDev, id0Dev, id1Dev, resultOffsetDev, and resultDistanceDev.
aclrtMalloc(&devPtr, resultNumDataLen, ACL_MEM_MALLOC_HUGE_FIRST);
resultNumDev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});
aclrtMalloc(&devPtr, dataLen, ACL_MEM_MALLOC_HUGE_FIRST);
id0Dev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});
aclrtMalloc(&devPtr, dataLen, ACL_MEM_MALLOC_HUGE_FIRST);
id1Dev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});
aclrtMalloc(&devPtr, dataLen, ACL_MEM_MALLOC_HUGE_FIRST);              
resultOffsetDev.reset(devPtr, [](void *p) {(void)aclrtFree(p);});
aclrtMalloc(&devPtr, dataLen, ACL_MEM_MALLOC_HUGE_FIRST);
resultDistanceDev.reset(devPtr, [](void *p) {(void)aclrtFree(p);}); 
 
//Create a search input table. The result is used as the input information for creating a search task.
aclfvQueryTable *searchQueryTable = aclfvCreateQueryTable(queryCnt, tableLen, reinterpret_cast<uint8_t *>
                                   (tableDataDev.get()), tableDataLen);
searchQueryTable.reset(searchQueryTable, [](aclfvQueryTable *p){(void)aclfvDestroyQueryTable(p);});

//Create a feature repository range parameter. The result is used as the input information for creating a search task.
aclfvRepoRange *searchRange = aclfvCreateRepoRange(0, 1023, 0, 1023); 
searchRange.reset(searchRange, [](aclfvRepoRange *p){(void)aclfvDestroyRepoRange(p);});

//Create the input information of a search task. The result is used for feature search in 1:N mode.
aclfvSearchInput *searchInput = aclfvCreateSearchInput(searchQueryTable, searchRange, topK);
searchInput.reset(searchInput, [](aclfvSearchInput *p){(void)aclfvDestroySearchInput(p);});

//Create the search result information. The result is used for feature search in 1:N mode.
aclfvSearchResult *searchResult = aclfvCreateSearchResult(queryCnt,
                                   reinterpret_cast<uint32_t *>(resultNumDev.get()),
                                   resultNumDataLen,
                                   reinterpret_cast<uint32_t *>(id0Dev.get()),
                                   reinterpret_cast<uint32_t *>(id1Dev.get()),
                                   reinterpret_cast<uint32_t *>(resultOffsetDev.get()),
                                   reinterpret_cast<float *>(resultDistanceDev.get()),
                                   dataLen);
searchResult.reset(searchResult, [](aclfvSearchResult *p){(void)aclfvDestroySearchResult(p);});

//5.2 Feature search in 1:N mode
aclfvSearch(SEARCH_1_N, searchInput.get(), searchResult.get());

//5.3 Process the feature search result.
//Obtain the search result.
uint32_t dataLen = queryCnt * topK * sizeof(uint32_t);
uint32_t *id0 = (uint32_t *)id0Dev.get();
uint32_t *id1 = (uint32_t *)id1Dev.get();
uint32_t *resultOffset= (uint32_t *)resultOffsetDev.get();
float *resultDistance = (float *)resultDistanceDev.get();

//If the run mode is ACL_HOST, the search result on the device needs to be sent back to the host through the aclrtMemcpy call.
if (aclrtGetRunMode(&runMode) == ACL_HOST) {
        //Copy data from the device to the host.
        id0 = (uint32_t *)malloc(dataLen);
        id0Ptr.reset(id0);
        id1 = (uint32_t *)malloc(dataLen);
        id1Ptr.reset(id1);
        resultOffset = (uint32_t *)malloc(dataLen);
        resultOffsetPtr.reset(resultOffset);
        resultDistance = (float *)malloc(dataLen);
        resultDistancePtr.reset(resultDistance);
        aclrtMemcpy(id0, dataLen, id0Dev.get(), dataLen, ACL_MEMCPY_DEVICE_TO_HOST);
        aclrtMemcpy(id1, dataLen, id0Dev.get(), dataLen, ACL_MEMCPY_DEVICE_TO_HOST);
        aclrtMemcpy(resultOffset, dataLen, resultOffsetDev.get(), dataLen, ACL_MEMCPY_DEVICE_TO_HOST);
        aclrtMemcpy(resultDistance, dataLen, resultDistanceDev.get(), dataLen, ACL_MEMCPY_DEVICE_TO_HOST);
}

//Display the data in the repository.
for (uint32_t i = 0; i < queryCnt; i++) {
     for (uint32_t j = 0; j < topK; ++j) {
          uint32_t i0 = id0[i * topK + j];
          uint32_t i1 = id1[i * topK + j];
          uint32_t offset = resultOffset[i * topK + j];
          float distance = resultDistance[i * topK + j];
    }
}

//6. Delete the repository and data.
//Create a feature repository range and delete the repository in the specified range.
uint32_t id0Min = 0;
uint32_t id0Max = 1023;
uint32_t id1Min = 0;
uint32_t id1Max = 1023;
aclfvRepoRange *repoRange = aclfvCreateRepoRange(id0Min, id0Max, id1Min, id1Max);
aclfvRepoDel(SEARCH_1_N, repoRange);
//Destroy data of the aclfvInitPara type.
aclfvDestroyInitPara(fvInitPara);

//7. Deallocate runtime resources.

//8. Perform deinitialization.
// ......