Skip to content

Commit b3b4cdc

Browse files
brandon-wadaAuto-format Bot
andauthored
Training test (#68)
* added test that training occurs after 2 yes 2 no * Automatically reformatting code * removed numpy requirements, added fix for human labeler disagreement * Automatically reformatting code --------- Co-authored-by: Auto-format Bot <autoformatbot@groundlight.ai>
1 parent 6d00943 commit b3b4cdc

File tree

2 files changed

+106
-42
lines changed

2 files changed

+106
-42
lines changed

test/assets/cat.jpeg

40.1 KB
Loading

test/integration/test_groundlight.py

Lines changed: 106 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,18 @@ def fixture_detector(gl: Groundlight) -> Detector:
4848
return gl.create_detector(name=name, query=query, pipeline_config=pipeline_config)
4949

5050

51-
@pytest.fixture(name="image_query")
52-
def fixture_image_query(gl: Groundlight, detector: Detector) -> ImageQuery:
51+
@pytest.fixture(name="image_query_yes")
52+
def fixture_image_query_yes(gl: Groundlight, detector: Detector) -> ImageQuery:
5353
iq = gl.submit_image_query(detector=detector.id, image="test/assets/dog.jpeg")
5454
return iq
5555

5656

57+
@pytest.fixture(name="image_query_no")
58+
def fixture_image_query_no(gl: Groundlight, detector: Detector) -> ImageQuery:
59+
iq = gl.submit_image_query(detector=detector.id, image="test/assets/cat.jpeg")
60+
return iq
61+
62+
5763
def test_create_detector(gl: Groundlight):
5864
name = f"Test {datetime.utcnow()}" # Need a unique name
5965
query = "Is there a dog?"
@@ -232,88 +238,89 @@ def test_list_image_queries(gl: Groundlight):
232238
assert is_valid_display_result(image_query.result)
233239

234240

235-
def test_get_image_query(gl: Groundlight, image_query: ImageQuery):
236-
_image_query = gl.get_image_query(id=image_query.id)
241+
def test_get_image_query(gl: Groundlight, image_query_yes: ImageQuery):
242+
_image_query = gl.get_image_query(id=image_query_yes.id)
237243
assert str(_image_query)
238244
assert isinstance(_image_query, ImageQuery)
239245
assert is_valid_display_result(_image_query.result)
240246

241247

242-
def test_get_image_query_label_yes(gl: Groundlight, image_query: ImageQuery):
243-
gl.add_label(image_query, Label.YES)
244-
retrieved_iq = gl.get_image_query(id=image_query.id)
248+
def test_get_image_query_label_yes(gl: Groundlight, image_query_yes: ImageQuery):
249+
gl.add_label(image_query_yes, Label.YES)
250+
retrieved_iq = gl.get_image_query(id=image_query_yes.id)
245251
assert retrieved_iq.result.label == Label.YES
246252

247253

248-
def test_get_image_query_label_no(gl: Groundlight, image_query: ImageQuery):
249-
gl.add_label(image_query, Label.NO)
250-
retrieved_iq = gl.get_image_query(id=image_query.id)
254+
def test_get_image_query_label_no(gl: Groundlight, image_query_no: ImageQuery):
255+
gl.add_label(image_query_no, Label.NO)
256+
retrieved_iq = gl.get_image_query(id=image_query_no.id)
251257
assert retrieved_iq.result.label == Label.NO
252258

253259

254-
def test_add_label_to_object(gl: Groundlight, image_query: ImageQuery):
255-
assert isinstance(image_query, ImageQuery)
256-
gl.add_label(image_query, Label.YES)
260+
def test_add_label_to_object(gl: Groundlight, image_query_yes: ImageQuery):
261+
assert isinstance(image_query_yes, ImageQuery)
262+
gl.add_label(image_query_yes, Label.YES)
257263

258264

259-
def test_add_label_by_id(gl: Groundlight, image_query: ImageQuery):
260-
iqid = image_query.id
265+
def test_add_label_by_id(gl: Groundlight, image_query_no: ImageQuery):
266+
iqid = image_query_no.id
261267
# TODO: Fully deprecate chk_ prefix
262268
assert iqid.startswith(("chk_", "iq_"))
263269
gl.add_label(iqid, Label.NO)
264270

265271

266-
def test_add_label_names(gl: Groundlight, image_query: ImageQuery):
267-
iqid = image_query.id
272+
def test_add_label_names(gl: Groundlight, image_query_yes: ImageQuery, image_query_no: ImageQuery):
273+
iqid_yes = image_query_yes.id
274+
iqid_no = image_query_no.id
268275

269276
# Valid labels
270-
gl.add_label(iqid, Label.YES)
271-
gl.add_label(iqid, Label.YES.value)
272-
gl.add_label(iqid, "YES")
273-
gl.add_label(iqid, "yes")
274-
gl.add_label(iqid, "yEs")
275-
gl.add_label(iqid, Label.NO)
276-
gl.add_label(iqid, Label.NO.value)
277-
gl.add_label(iqid, "NO")
278-
gl.add_label(iqid, "no")
277+
gl.add_label(iqid_yes, Label.YES)
278+
gl.add_label(iqid_yes, Label.YES.value)
279+
gl.add_label(iqid_yes, "YES")
280+
gl.add_label(iqid_yes, "yes")
281+
gl.add_label(iqid_yes, "yEs")
282+
gl.add_label(iqid_no, Label.NO)
283+
gl.add_label(iqid_no, Label.NO.value)
284+
gl.add_label(iqid_no, "NO")
285+
gl.add_label(iqid_no, "no")
279286

280287
# Invalid labels
281288
with pytest.raises(ValueError):
282-
gl.add_label(iqid, "PASS")
289+
gl.add_label(iqid_yes, "PASS")
283290
with pytest.raises(ValueError):
284-
gl.add_label(iqid, "FAIL")
291+
gl.add_label(iqid_yes, "FAIL")
285292
with pytest.raises(ValueError):
286-
gl.add_label(iqid, DeprecatedLabel.PASS)
293+
gl.add_label(iqid_yes, DeprecatedLabel.PASS)
287294
with pytest.raises(ValueError):
288-
gl.add_label(iqid, DeprecatedLabel.FAIL)
295+
gl.add_label(iqid_yes, DeprecatedLabel.FAIL)
289296
with pytest.raises(ValueError):
290-
gl.add_label(iqid, "sorta")
297+
gl.add_label(iqid_yes, "sorta")
291298
with pytest.raises(ValueError):
292-
gl.add_label(iqid, "YES ")
299+
gl.add_label(iqid_yes, "YES ")
293300
with pytest.raises(ValueError):
294-
gl.add_label(iqid, " YES")
301+
gl.add_label(iqid_yes, " YES")
295302
with pytest.raises(ValueError):
296-
gl.add_label(iqid, "0")
303+
gl.add_label(iqid_yes, "0")
297304
with pytest.raises(ValueError):
298-
gl.add_label(iqid, "1")
305+
gl.add_label(iqid_yes, "1")
299306

300307
# We technically don't allow these in the type signature, but users might do it anyway
301308
with pytest.raises(ValueError):
302-
gl.add_label(iqid, 0) # type: ignore
309+
gl.add_label(iqid_yes, 0) # type: ignore
303310
with pytest.raises(ValueError):
304-
gl.add_label(iqid, 1) # type: ignore
311+
gl.add_label(iqid_yes, 1) # type: ignore
305312
with pytest.raises(ValueError):
306-
gl.add_label(iqid, None) # type: ignore
313+
gl.add_label(iqid_yes, None) # type: ignore
307314
with pytest.raises(ValueError):
308-
gl.add_label(iqid, True) # type: ignore
315+
gl.add_label(iqid_yes, True) # type: ignore
309316
with pytest.raises(ValueError):
310-
gl.add_label(iqid, False) # type: ignore
317+
gl.add_label(iqid_yes, False) # type: ignore
311318
with pytest.raises(ValueError):
312-
gl.add_label(iqid, b"YES") # type: ignore
319+
gl.add_label(iqid_yes, b"YES") # type: ignore
313320

314321
# We may want to support something like this in the future, but not yet
315322
with pytest.raises(ValueError):
316-
gl.add_label(iqid, Label.UNSURE)
323+
gl.add_label(iqid_yes, Label.UNSURE)
317324

318325

319326
def test_label_conversion_produces_strings():
@@ -337,3 +344,60 @@ def test_submit_numpy_image(gl: Groundlight, detector: Detector):
337344
assert str(_image_query)
338345
assert isinstance(_image_query, ImageQuery)
339346
assert is_valid_display_result(_image_query.result)
347+
348+
349+
@pytest.mark.skipif(MISSING_PIL, reason="Needs pillow") # type: ignore
350+
def test_detector_improvement(gl: Groundlight):
351+
# test that we get confidence improvement after sending images in
352+
# Pass two of each type of image in
353+
import random
354+
import time
355+
356+
from PIL import Image, ImageEnhance
357+
358+
random.seed(2741)
359+
360+
name = f"Test {datetime.utcnow()}" # Need a unique name
361+
query = "Is there a dog?"
362+
detector = gl.create_detector(name=name, query=query)
363+
364+
def submit_noisy_image(image, label=None):
365+
sharpness = ImageEnhance.Sharpness(image)
366+
noisy_image = sharpness.enhance(random.uniform(0.75, 1.25))
367+
color = ImageEnhance.Color(noisy_image)
368+
noisy_image = color.enhance(random.uniform(0.75, 1))
369+
contrast = ImageEnhance.Contrast(noisy_image)
370+
noisy_image = contrast.enhance(random.uniform(0.75, 1))
371+
brightness = ImageEnhance.Brightness(noisy_image)
372+
noisy_image = brightness.enhance(random.uniform(0.75, 1))
373+
img_query = gl.submit_image_query(detector=detector.id, image=noisy_image, wait=0)
374+
if label is not None:
375+
gl.add_label(img_query, label)
376+
return img_query
377+
378+
dog = Image.open("test/assets/dog.jpeg")
379+
cat = Image.open("test/assets/cat.jpeg")
380+
381+
submit_noisy_image(dog, "YES")
382+
submit_noisy_image(dog, "YES")
383+
submit_noisy_image(cat, "NO")
384+
submit_noisy_image(cat, "NO")
385+
386+
# wait to give enough time to train
387+
wait_period = 30 # seconds
388+
num_wait_periods = 4 # 2 minutes total
389+
for _ in range(num_wait_periods):
390+
time.sleep(wait_period)
391+
new_dog_query = submit_noisy_image(dog)
392+
new_cat_query = submit_noisy_image(cat)
393+
if new_cat_query.result.confidence < 0.6 or new_cat_query.result.label == "YES":
394+
# If the new query is not confident enough, we'll try again
395+
continue
396+
elif new_dog_query.result.confidence < 0.6 or new_dog_query.result.label == "NO":
397+
# If the new query is not confident enough, we'll try again
398+
continue
399+
else:
400+
assert True
401+
return
402+
403+
assert False, "The detector performance has not improved after two minutes"

0 commit comments

Comments
 (0)