Skip to content

Commit fd09e51

Browse files
mdouzefacebook-github-bot
authored andcommitted
move by_residual to IndexIVF (facebookresearch#2870)
Summary: Pull Request resolved: facebookresearch#2870 Factor by_residual for all the IndexIVF inheritors. Some training code can be put in IndexIVF and `train_residual` is replaced with `train_encoder`. This will be used for the IndependentQuantizer work. Reviewed By: alexanderguzhva Differential Revision: D45987304 fbshipit-source-id: 7310a687b556b2faa15a76456b1d9000e21b58ce
1 parent 1c1879b commit fd09e51

29 files changed

+224
-269
lines changed

c_api/IndexIVF_c.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,17 @@ void faiss_IndexIVF_invlists_get_ids(
165165
memcpy(invlist, list, list_size * sizeof(idx_t));
166166
}
167167

168+
int faiss_IndexIVF_train_encoder(
169+
FaissIndexIVF* index,
170+
idx_t n,
171+
const float* x,
172+
const idx_t* assign) {
173+
try {
174+
reinterpret_cast<IndexIVF*>(index)->train_encoder(n, x, assign);
175+
}
176+
CATCH_AND_HANDLE
177+
}
178+
168179
void faiss_IndexIVFStats_reset(FaissIndexIVFStats* stats) {
169180
reinterpret_cast<IndexIVFStats*>(stats)->reset();
170181
}

c_api/IndexIVF_c.h

+6
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,12 @@ void faiss_IndexIVF_invlists_get_ids(
154154
size_t list_no,
155155
idx_t* invlist);
156156

157+
int faiss_IndexIVF_train_encoder(
158+
FaissIndexIVF* index,
159+
idx_t n,
160+
const float* x,
161+
const idx_t* assign);
162+
157163
typedef struct FaissIndexIVFStats {
158164
size_t nq; // nb of queries run
159165
size_t nlist; // nb of inverted lists scanned

c_api/IndexScalarQuantizer_c.cpp

-10
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,3 @@ int faiss_IndexIVFScalarQuantizer_add_core(
110110
}
111111
CATCH_AND_HANDLE
112112
}
113-
114-
int faiss_IndexIVFScalarQuantizer_train_residual(
115-
FaissIndexIVFScalarQuantizer* index,
116-
idx_t n,
117-
const float* x) {
118-
try {
119-
reinterpret_cast<IndexIVFScalarQuantizer*>(index)->train_residual(n, x);
120-
}
121-
CATCH_AND_HANDLE
122-
}

c_api/IndexScalarQuantizer_c.h

-5
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,6 @@ int faiss_IndexIVFScalarQuantizer_add_core(
8888
const idx_t* xids,
8989
const idx_t* precomputed_idx);
9090

91-
int faiss_IndexIVFScalarQuantizer_train_residual(
92-
FaissIndexIVFScalarQuantizer* index,
93-
idx_t n,
94-
const float* x);
95-
9691
#ifdef __cplusplus
9792
}
9893
#endif

faiss/IndexIVF.cpp

+36-6
Original file line numberDiff line numberDiff line change
@@ -1061,22 +1061,52 @@ void IndexIVF::update_vectors(int n, const idx_t* new_ids, const float* x) {
10611061
}
10621062

10631063
void IndexIVF::train(idx_t n, const float* x) {
1064-
if (verbose)
1064+
if (verbose) {
10651065
printf("Training level-1 quantizer\n");
1066+
}
10661067

10671068
train_q1(n, x, verbose, metric_type);
10681069

1069-
if (verbose)
1070+
if (verbose) {
10701071
printf("Training IVF residual\n");
1072+
}
1073+
1074+
// optional subsampling
1075+
idx_t max_nt = train_encoder_num_vectors();
1076+
if (max_nt <= 0) {
1077+
max_nt = (size_t)1 << 35;
1078+
}
1079+
1080+
TransformedVectors tv(
1081+
x, fvecs_maybe_subsample(d, (size_t*)&n, max_nt, x, verbose));
1082+
1083+
if (by_residual) {
1084+
std::vector<idx_t> assign(n);
1085+
quantizer->assign(n, tv.x, assign.data());
1086+
1087+
std::vector<float> residuals(n * d);
1088+
quantizer->compute_residual_n(n, tv.x, residuals.data(), assign.data());
1089+
1090+
train_encoder(n, residuals.data(), assign.data());
1091+
} else {
1092+
train_encoder(n, tv.x, nullptr);
1093+
}
10711094

1072-
train_residual(n, x);
10731095
is_trained = true;
10741096
}
10751097

1076-
void IndexIVF::train_residual(idx_t /*n*/, const float* /*x*/) {
1077-
if (verbose)
1078-
printf("IndexIVF: no residual training\n");
1098+
idx_t IndexIVF::train_encoder_num_vectors() const {
1099+
return 0;
1100+
}
1101+
1102+
void IndexIVF::train_encoder(
1103+
idx_t /*n*/,
1104+
const float* /*x*/,
1105+
const idx_t* assign) {
10791106
// does nothing by default
1107+
if (verbose) {
1108+
printf("IndexIVF: no residual training\n");
1109+
}
10801110
}
10811111

10821112
bool check_compatible_for_merge_expensive_check = true;

faiss/IndexIVF.h

+15-4
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ struct IndexIVF : Index, IndexIVFInterface {
177177
bool own_invlists = false;
178178

179179
size_t code_size = 0; ///< code size per vector in bytes
180+
180181
/** Parallel mode determines how queries are parallelized with OpenMP
181182
*
182183
* 0 (default): split over queries
@@ -194,6 +195,10 @@ struct IndexIVF : Index, IndexIVFInterface {
194195
* enables reconstruct() */
195196
DirectMap direct_map;
196197

198+
/// do the codes in the invlists encode the vectors relative to the
199+
/// centroids?
200+
bool by_residual = true;
201+
197202
/** The Inverted file takes a quantizer (an Index) on input,
198203
* which implements the function mapping a vector to a list
199204
* identifier.
@@ -207,7 +212,7 @@ struct IndexIVF : Index, IndexIVFInterface {
207212

208213
void reset() override;
209214

210-
/// Trains the quantizer and calls train_residual to train sub-quantizers
215+
/// Trains the quantizer and calls train_encoder to train sub-quantizers
211216
void train(idx_t n, const float* x) override;
212217

213218
/// Calls add_with_ids with NULL ids
@@ -252,9 +257,15 @@ struct IndexIVF : Index, IndexIVFInterface {
252257
*/
253258
void add_sa_codes(idx_t n, const uint8_t* codes, const idx_t* xids);
254259

255-
/// Sub-classes that encode the residuals can train their encoders here
256-
/// does nothing by default
257-
virtual void train_residual(idx_t n, const float* x);
260+
/** Train the encoder for the vectors.
261+
*
262+
* If by_residual then it is called with residuals and corresponding assign
263+
* array, otherwise x is the raw training vectors and assign=nullptr */
264+
virtual void train_encoder(idx_t n, const float* x, const idx_t* assign);
265+
266+
/// can be redefined by subclasses to indicate how many training vectors
267+
/// they need
268+
virtual idx_t train_encoder_num_vectors() const;
258269

259270
void search_preassigned(
260271
idx_t n,

faiss/IndexIVFAdditiveQuantizer.cpp

+8-18
Original file line numberDiff line numberDiff line change
@@ -37,30 +37,20 @@ IndexIVFAdditiveQuantizer::IndexIVFAdditiveQuantizer(
3737
IndexIVFAdditiveQuantizer::IndexIVFAdditiveQuantizer(AdditiveQuantizer* aq)
3838
: IndexIVF(), aq(aq) {}
3939

40-
void IndexIVFAdditiveQuantizer::train_residual(idx_t n, const float* x) {
41-
const float* x_in = x;
40+
void IndexIVFAdditiveQuantizer::train_encoder(
41+
idx_t n,
42+
const float* x,
43+
const idx_t* assign) {
44+
aq->train(n, x);
45+
}
4246

47+
idx_t IndexIVFAdditiveQuantizer::train_encoder_num_vectors() const {
4348
size_t max_train_points = 1024 * ((size_t)1 << aq->nbits[0]);
4449
// we need more data to train LSQ
4550
if (dynamic_cast<LocalSearchQuantizer*>(aq)) {
4651
max_train_points = 1024 * aq->M * ((size_t)1 << aq->nbits[0]);
4752
}
48-
49-
x = fvecs_maybe_subsample(
50-
d, (size_t*)&n, max_train_points, x, verbose, 1234);
51-
ScopeDeleter<float> del_x(x_in == x ? nullptr : x);
52-
53-
if (by_residual) {
54-
std::vector<idx_t> idx(n);
55-
quantizer->assign(n, x, idx.data());
56-
57-
std::vector<float> residuals(n * d);
58-
quantizer->compute_residual_n(n, x, residuals.data(), idx.data());
59-
60-
aq->train(n, residuals.data());
61-
} else {
62-
aq->train(n, x);
63-
}
53+
return max_train_points;
6454
}
6555

6656
void IndexIVFAdditiveQuantizer::encode_vectors(

faiss/IndexIVFAdditiveQuantizer.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ namespace faiss {
2626
struct IndexIVFAdditiveQuantizer : IndexIVF {
2727
// the quantizer
2828
AdditiveQuantizer* aq;
29-
bool by_residual = true;
3029
int use_precomputed_table = 0; // for future use
3130

3231
using Search_type_t = AdditiveQuantizer::Search_type_t;
@@ -40,7 +39,9 @@ struct IndexIVFAdditiveQuantizer : IndexIVF {
4039

4140
explicit IndexIVFAdditiveQuantizer(AdditiveQuantizer* aq);
4241

43-
void train_residual(idx_t n, const float* x) override;
42+
void train_encoder(idx_t n, const float* x, const idx_t* assign) override;
43+
44+
idx_t train_encoder_num_vectors() const override;
4445

4546
void encode_vectors(
4647
idx_t n,

faiss/IndexIVFAdditiveQuantizerFastScan.cpp

+10-36
Original file line numberDiff line numberDiff line change
@@ -131,45 +131,20 @@ IndexIVFAdditiveQuantizerFastScan::~IndexIVFAdditiveQuantizerFastScan() {}
131131
* Training
132132
*********************************************************/
133133

134-
void IndexIVFAdditiveQuantizerFastScan::train_residual(
134+
idx_t IndexIVFAdditiveQuantizerFastScan::train_encoder_num_vectors() const {
135+
return max_train_points;
136+
}
137+
138+
void IndexIVFAdditiveQuantizerFastScan::train_encoder(
135139
idx_t n,
136-
const float* x_in) {
140+
const float* x,
141+
const idx_t* assign) {
137142
if (aq->is_trained) {
138143
return;
139144
}
140145

141-
const int seed = 0x12345;
142-
size_t nt = n;
143-
const float* x = fvecs_maybe_subsample(
144-
d, &nt, max_train_points, x_in, verbose, seed);
145-
n = nt;
146146
if (verbose) {
147-
printf("training additive quantizer on %zd vectors\n", nt);
148-
}
149-
aq->verbose = verbose;
150-
151-
std::unique_ptr<float[]> del_x;
152-
if (x != x_in) {
153-
del_x.reset((float*)x);
154-
}
155-
156-
const float* trainset;
157-
std::vector<float> residuals(n * d);
158-
std::vector<idx_t> assign(n);
159-
160-
if (by_residual) {
161-
if (verbose) {
162-
printf("computing residuals\n");
163-
}
164-
quantizer->assign(n, x, assign.data());
165-
residuals.resize(n * d);
166-
for (idx_t i = 0; i < n; i++) {
167-
quantizer->compute_residual(
168-
x + i * d, residuals.data() + i * d, assign[i]);
169-
}
170-
trainset = residuals.data();
171-
} else {
172-
trainset = x;
147+
printf("training additive quantizer on %d vectors\n", int(n));
173148
}
174149

175150
if (verbose) {
@@ -181,17 +156,16 @@ void IndexIVFAdditiveQuantizerFastScan::train_residual(
181156
d);
182157
}
183158
aq->verbose = verbose;
184-
aq->train(n, trainset);
159+
aq->train(n, x);
185160

186161
// train norm quantizer
187162
if (by_residual && metric_type == METRIC_L2) {
188163
std::vector<float> decoded_x(n * d);
189164
std::vector<uint8_t> x_codes(n * aq->code_size);
190-
aq->compute_codes(residuals.data(), x_codes.data(), n);
165+
aq->compute_codes(x, x_codes.data(), n);
191166
aq->decode(x_codes.data(), decoded_x.data(), n);
192167

193168
// add coarse centroids
194-
FAISS_THROW_IF_NOT(assign.size() == n);
195169
std::vector<float> centroid(d);
196170
for (idx_t i = 0; i < n; i++) {
197171
auto xi = decoded_x.data() + i * d;

faiss/IndexIVFAdditiveQuantizerFastScan.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ struct IndexIVFAdditiveQuantizerFastScan : IndexIVFFastScan {
6363
const IndexIVFAdditiveQuantizer& orig,
6464
int bbs = 32);
6565

66-
void train_residual(idx_t n, const float* x) override;
66+
void train_encoder(idx_t n, const float* x, const idx_t* assign) override;
67+
68+
idx_t train_encoder_num_vectors() const override;
6769

6870
void estimate_norm_scale(idx_t n, const float* x);
6971

faiss/IndexIVFFastScan.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,16 @@ IndexIVFFastScan::IndexIVFFastScan(
4343
size_t code_size,
4444
MetricType metric)
4545
: IndexIVF(quantizer, d, nlist, code_size, metric) {
46+
// unlike other indexes, we prefer no residuals for performance reasons.
47+
by_residual = false;
4648
FAISS_THROW_IF_NOT(metric == METRIC_L2 || metric == METRIC_INNER_PRODUCT);
4749
}
4850

4951
IndexIVFFastScan::IndexIVFFastScan() {
5052
bbs = 0;
5153
M2 = 0;
5254
is_trained = false;
55+
by_residual = false;
5356
}
5457

5558
void IndexIVFFastScan::init_fastscan(

faiss/IndexIVFFastScan.h

-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ struct IndexIVFFastScan : IndexIVF {
4545
int implem = 0;
4646
// skip some parts of the computation (for timing)
4747
int skip = 0;
48-
bool by_residual = false;
4948

5049
// batching factors at search time (0 = default)
5150
int qbs = 0;

faiss/IndexIVFFlat.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ IndexIVFFlat::IndexIVFFlat(
3636
MetricType metric)
3737
: IndexIVF(quantizer, d, nlist, sizeof(float) * d, metric) {
3838
code_size = sizeof(float) * d;
39+
by_residual = false;
40+
}
41+
42+
IndexIVFFlat::IndexIVFFlat() {
43+
by_residual = false;
3944
}
4045

4146
void IndexIVFFlat::add_core(
@@ -45,6 +50,7 @@ void IndexIVFFlat::add_core(
4550
const int64_t* coarse_idx) {
4651
FAISS_THROW_IF_NOT(is_trained);
4752
FAISS_THROW_IF_NOT(coarse_idx);
53+
FAISS_THROW_IF_NOT(!by_residual);
4854
assert(invlists);
4955
direct_map.check_can_add(xids);
5056

@@ -89,6 +95,7 @@ void IndexIVFFlat::encode_vectors(
8995
const idx_t* list_nos,
9096
uint8_t* codes,
9197
bool include_listnos) const {
98+
FAISS_THROW_IF_NOT(!by_residual);
9299
if (!include_listnos) {
93100
memcpy(codes, x, code_size * n);
94101
} else {

faiss/IndexIVFFlat.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct IndexIVFFlat : IndexIVF {
5050

5151
void sa_decode(idx_t n, const uint8_t* bytes, float* x) const override;
5252

53-
IndexIVFFlat() {}
53+
IndexIVFFlat();
5454
};
5555

5656
struct IndexIVFFlatDedup : IndexIVFFlat {

0 commit comments

Comments
 (0)