Skip to content

Commit 159f8da

Browse files
authored
Merge pull request #222 from ipums/classifier_seeds
Adjust how choose_classifier handles seed parameters
2 parents 423e231 + d5ee25e commit 159f8da

3 files changed

Lines changed: 36 additions & 5 deletions

File tree

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
ARG PYTHON_VERSION=3.10
2-
FROM python:${PYTHON_VERSION}
2+
FROM python:${PYTHON_VERSION}-bookworm
33
ARG HLINK_EXTRAS=dev
44

5-
RUN apt-get update && apt-get install default-jre-headless -y
5+
RUN apt-get update && apt-get install openjdk-17-jre-headless -y
66

77
RUN mkdir /hlink
88
WORKDIR /hlink

hlink/linking/core/classifier.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@
3030
_xgboost_available = True
3131

3232

33+
def _ensure_seeded(params: dict[str, Any]) -> dict[str, Any]:
34+
"""
35+
Ensure that the given dictionary of parameters has a "seed" parameter.
36+
37+
This is useful for making results reproducible across different linking
38+
runs. If the user doesn't set the "seed" parameter, then this function sets
39+
it to 2133 (which is just as good as any other number, I suppose).
40+
"""
41+
return {"seed": 2133, **params}
42+
43+
3344
def choose_classifier(model_type: str, params: dict[str, Any], dep_var: str):
3445
"""Given a model type and hyper-parameters for the model, return a
3546
classifier of that type with those hyper-parameters, along with a
@@ -60,11 +71,11 @@ def choose_classifier(model_type: str, params: dict[str, Any], dep_var: str):
6071
post_transformer = SQLTransformer(statement="SELECT * FROM __THIS__")
6172
features_vector = "features_vector"
6273
if model_type == "random_forest":
74+
params = _ensure_seeded(params)
6375
classifier = RandomForestClassifier(
6476
**params,
6577
labelCol=dep_var,
6678
featuresCol=features_vector,
67-
seed=2133,
6879
probabilityCol="probability_array",
6980
)
7081
post_transformer = SQLTransformer(
@@ -93,23 +104,23 @@ def choose_classifier(model_type: str, params: dict[str, Any], dep_var: str):
93104
)
94105

95106
elif model_type == "decision_tree":
107+
params = _ensure_seeded(params)
96108
classifier = DecisionTreeClassifier(
97109
**params,
98110
featuresCol=features_vector,
99111
labelCol=dep_var,
100112
probabilityCol="probability_array",
101-
seed=2133,
102113
)
103114
post_transformer = SQLTransformer(
104115
statement="SELECT *, parseProbVector(probability_array, 1) as probability FROM __THIS__"
105116
)
106117

107118
elif model_type == "gradient_boosted_trees":
119+
params = _ensure_seeded(params)
108120
classifier = GBTClassifier(
109121
**params,
110122
featuresCol=features_vector,
111123
labelCol=dep_var,
112-
seed=2133,
113124
)
114125
post_transformer = (
115126
hlink.linking.transformers.rename_prob_column.RenameProbColumn()
@@ -122,6 +133,8 @@ def choose_classifier(model_type: str, params: dict[str, Any], dep_var: str):
122133
"its dependencies. Try installing hlink with the lightgbm extra: "
123134
"\n\n pip install hlink[lightgbm]"
124135
)
136+
137+
params = _ensure_seeded(params)
125138
classifier = synapse.ml.lightgbm.LightGBMClassifier(
126139
**params,
127140
featuresCol=features_vector,
@@ -138,6 +151,8 @@ def choose_classifier(model_type: str, params: dict[str, Any], dep_var: str):
138151
"the xgboost library and its dependencies. Try installing hlink with "
139152
"the xgboost extra:\n\n pip install hlink[xgboost]"
140153
)
154+
155+
params = _ensure_seeded(params)
141156
classifier = xgboost.spark.SparkXGBClassifier(
142157
**params,
143158
features_col=features_vector,

hlink/tests/core/classifier_test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
# in this project's top-level directory, and also on-line at:
44
# https://github.com/ipums/hlink
55

6+
import pytest
7+
68
from hlink.linking.core.classifier import choose_classifier
79
from hlink.tests.markers import requires_lightgbm, requires_xgboost
810

@@ -30,3 +32,17 @@ def test_choose_classifier_supports_xgboost():
3032
}
3133
classifier, _post_transformer = choose_classifier("xgboost", params, "match")
3234
assert classifier.getLabelCol() == "match"
35+
36+
37+
@pytest.mark.parametrize(
38+
"classifier", ["random_forest", "decision_tree", "gradient_boosted_trees"]
39+
)
40+
def test_choose_classifier_can_set_seed_in_params(spark, classifier) -> None:
41+
"""
42+
Ensure that you can pass a "seed" parameter to the classifier. This used to
43+
cause an error because of manual handling of the seed parameter. See GitHub
44+
Issue #221.
45+
"""
46+
params = {"seed": 151015}
47+
classifier, _post_transformer = choose_classifier(classifier, params, "match")
48+
assert classifier.getSeed() == 151015

0 commit comments

Comments
 (0)