Skip to content

Commit 6ef2889

Browse files
mdouzefacebook-github-bot
authored andcommitted
move by_residual to IndexIVF
Summary: 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: 5b777fe3e6ecbbd46df4f48475eea974ef2c935b
1 parent 5c221ed commit 6ef2889

25 files changed

+201
-253
lines changed

faiss/IndexIVF.cpp

Lines changed: 36 additions & 6 deletions
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

Lines changed: 15 additions & 4 deletions
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

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,30 +37,17 @@ 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(idx_t n, const float* x, const idx_t* assign) {
41+
aq->train(n, x);
42+
}
4243

44+
idx_t IndexIVFAdditiveQuantizer::train_encoder_num_vectors() const {
4345
size_t max_train_points = 1024 * ((size_t)1 << aq->nbits[0]);
4446
// we need more data to train LSQ
4547
if (dynamic_cast<LocalSearchQuantizer*>(aq)) {
4648
max_train_points = 1024 * aq->M * ((size_t)1 << aq->nbits[0]);
4749
}
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-
}
50+
return max_train_points;
6451
}
6552

6653
void IndexIVFAdditiveQuantizer::encode_vectors(

faiss/IndexIVFAdditiveQuantizer.h

Lines changed: 3 additions & 2 deletions
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

Lines changed: 10 additions & 36 deletions
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

Lines changed: 3 additions & 1 deletion
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 0 additions & 1 deletion
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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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)