Feature Vector Search
For the
For the
For the
For the
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.
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. // ...... |