I came across different approaches to creating a test set. Theoretically, it's quite simple, just pick some instances randomly, typically 20% of the dataset and set them aside. Below are the approaches
The naive way of creating the test set is
def split_train_test(data,test_set_ratio):
#create indices
shuffled_indices = np.random.permutation(len(data))
test_set_size = int(len(data) * test_set_ratio)
test_set_indices = shuffled_indices[:test_set_size]
train_set_indices = shuffled_indices[test_set_size:]
return data.iloc[train_set_indices],data.iloc[test_set_indices]
The above splitting mechanism works, but if the program is run, again and again, it will generate a different dataset. Over the time, the machine learning algorithm will get to see all the examples. The solutions to fix the above problem was (guided by the author of the book)
- Save the test set on the first run and then load it in subsequent runs
- To set the random number generator's seed(np.random.seed(42)) before calling np.random.permutation() so that it always generates the same shuffled indices
But both the above solutions break when we fetch the next updated dataset. I am not still clear with this statement.
Can someone give me an intuition behind how do the above two solutions breaks when we fetch the next updated dataset ?.
Then the author came up with another reliable approach to create the test.
def split_train_test_by_id(data, test_ratio, id_column):
ids = data[id_column]
in_test_set = ids.apply(lambda id_: test_set_check(id_, test_ratio))
return data.loc[~in_test_set], data.loc[in_test_set]
Approach #1
def test_set_check(identifier, test_ratio, hash=hashlib.md5):
return bytearray(hash(np.int64(identifier)).digest())[-1] < 256 * test_ratio
Approach #2
def test_set_check(identifier, test_ratio):
return crc32(np.int64(identifier)) & 0xffffffff < test_ratio * 2**32
Approaches #1,#2, why are we making use of crc32, 0xffffffff, byte array?.
Just out of curiosity, I passed different values for identifier variable into hash_function(np.int64(identifier)).digest()
and I got different results.
Is there any intuition behind these results ?.