Feature Vector Search (FV)
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 or 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
The sample code in this section focuses on the code logic of feature vector search. It is for reference only. Do not copy, build, or run it directly. You can 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.
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 | // Set the default run mode to ACL_HOST. aclrtRunMode runMode = ACL_HOST; // 1. Perform initialization. // 1.1 Initialize the feature search module. The following uses the repository with 100000 features as an example. size_t fsNum = 100000; fvInitPara = aclfvCreateInitPara(fsNum); // 1.2 Specify the initialization parameter for feature vector search. ret = aclfvInit(fvInitPara); // 2. Add the repository and feature vectors. // 2.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 and call aclrtMemcpy to transfer the random feature data from the host to the device. In other modes, directly load the random feature data into the device memory. if (aclrtGetRunMode(&runMode) == ACL_HOST) { // Allocate memory for inputData. ret = aclrtMalloc(&inputData, featureLen * featureCount, ACL_MEM_MALLOC_HUGE_FIRST); // Load the random feature data into the device memory. inputDataPtr.reset(inputData, [](void *p) {(void)aclrtFree(p);}); // Copy featureData 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); // 2.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 the code in 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, skip this step. dataLen indicates the length of the memory allocated to 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); // 2.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 the code in step 2.1. // .... // 3. Repository search (in 1:N mode for example), including feature search preprocessing, 1:N feature search, and feature search result processing. // 3.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 a search input table. 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);}); // 3.2 Feature search in 1:N mode aclfvSearch(SEARCH_1_N, searchInput.get(), searchResult.get()); // 3.3 Processing 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 API. 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]; } } // 4. 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); ...... |
Parent topic: Dedicated Accelerator