Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Image by Step" plots #4372

Merged
merged 25 commits into from
Aug 4, 2023

Conversation

julieg18
Copy link
Contributor

@julieg18 julieg18 commented Jul 27, 2023

@julieg18 julieg18 added the product PR that affects product label Jul 27, 2023
@julieg18 julieg18 self-assigned this Jul 27, 2023
@julieg18 julieg18 changed the base branch from main to add-comparison-multi-image-plot July 27, 2023 22:22
const multiImagePath = joinFunc('plots', 'image')
const isMulti = path.includes(multiImagePath)
const pathLabel = path
const pathLabel = path.includes('image') ? join('plots', 'image') : path
Copy link
Contributor Author

@julieg18 julieg18 Jul 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had to use join here so the tests would pass on windows (we rebuild the image path when collecting the plots).

extension/src/plots/paths/model.ts Outdated Show resolved Hide resolved
onClick={() => zoomPlot(url)}
data-testid="image-plot-button"
>
<img
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Things that I thought could be done in a followup:

  • Currently we show the step based off the index of the array which wouldn't work correctly if the user deselected some of the images in the plots sideview tree. A better way to calculate the step could be relying on the image file name or getting the index when collecting the images on the backend.
  • Save the multiValues across sessions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently we show the step based off the index of the array which wouldn't work correctly if the user deselected some of the images in the plots sideview tree. A better way to calculate the step could be relying on the image file name or getting the index when collecting the images on the backend.

Should we treat the whole thing as a single plot and not allowed selecting/de-selecting of steps?

Save the multiValues across sessions.

Are the plot steps reset when an experiment runs, or is that handled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we treat the whole thing as a single plot and not allowed selecting/de-selecting of steps?

Yes, we could do that! That would make image and image path collection easier! Apologies if that was your original idea in #4319 (comment), I misunderstood 😊

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It definitely wasn't the original idea but let's try and get rid of isMulti as a flag if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted the code to turn off selection/de-selection of steps.

Are the plot steps reset when an experiment runs, or is that handled?

Nope, the plot steps would only reset if you close the "session". They'll keep their current value during an experiment run :)

@mattseddon
Copy link
Member

@julieg18 I've tried updating train.py in the demo project to log an image per step but it isn't getting picked up by the extension, is this expected? Does there need to be updates made to DVCLive/DVC to get this to work?

@julieg18
Copy link
Contributor Author

@julieg18 I've tried updating train.py in the demo project to log an image per step but it isn't getting picked up by the extension, is this expected? Does there need to be updates made to DVCLive/DVC to get this to work?

What change did you make?

This was the change I made to get the images:

 # Iterate over training epochs.
    for epoch in range(params["epochs"]):
        # ...rest of code 
        live.log_image(f"image/{epoch}.jpg", missclassified)

@mattseddon
Copy link
Member

@julieg18 I've tried updating train.py in the demo project to log an image per step but it isn't getting picked up by the extension, is this expected? Does there need to be updates made to DVCLive/DVC to get this to work?

What change did you make?

This was the change I made to get the images:

 # Iterate over training epochs.
    for epoch in range(params["epochs"]):
        # ...rest of code 
        live.log_image(f"image/{epoch}.jpg", missclassified)

Yeah, that is the change I made: live.log_image(f"misclassified/{epoch}.jpg", missclassified)

☝🏻 is image being in the path hard-coded to identify multi-step plots?

@julieg18
Copy link
Contributor Author

Yeah, that is the change I made: live.log_image(f"misclassified/{epoch}.jpg", misclassified)
☝🏻 is image being in the path hard-coded to identify multi-step plots?

Yes, that is what was decided in iterative/dvclive#425. You'd need to put image as the directory.

@mattseddon
Copy link
Member

Yeah, that is the change I made: live.log_image(f"misclassified/{epoch}.jpg", misclassified)
☝🏻 is image being in the path hard-coded to identify multi-step plots?

Yes, that is what was decided in iterative/dvclive#425. You'd need to put image as the directory.

Can you please double-check that image was not just a placeholder for the actual image name? What if there are two images that you want to save for each step? Would you have to do:

misclassified/image/{step} & other_image/image/{step}

?

@julieg18
Copy link
Contributor Author

Can you please double-check that image was not just a placeholder for the actual image name? What if there are two images that you want to save for each step?

Oh, you're right, that's probably what was implied and I took things too literally 🤦‍♀️. I'll adjust the check we use.

extension/src/plots/model/collect.ts Outdated Show resolved Hide resolved
for (const [label, paths] of Object.entries(acc.comparisonData)) {
for (const path of Object.keys(paths)) {
acc.comparisonData[label][path].sort((img1, img2) =>
img1.path.localeCompare(img2.path, undefined, { numeric: true })
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dvc plots diff doesn't give you directly images in the step order so we need to sort them on our end.

onClick={() => zoomPlot(url)}
data-testid="image-plot-button"
>
<img
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted the code to turn off selection/de-selection of steps.

Are the plot steps reset when an experiment runs, or is that handled?

Nope, the plot steps would only reset if you close the "session". They'll keep their current value during an experiment run :)

.multiImageWrapper {
.image,
.noImageContent {
height: 380px;
Copy link
Contributor Author

@julieg18 julieg18 Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to have a fixed height for multi images since we need all images in the multi plots to be the same height:

Screen.Recording.2023-07-31.at.12.34.10.PM.mov

There should be some ways to at least get an average height based off the images instead of just using a fixed number or apply some CSS that keeps the imgs stable but I figured it could be saved for a followup.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really weird. Definitely need to fix.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really weird. Definitely need to fix.

Understood! I didn't think it looked that bad, but if you think it looks really weird, I'm happy to work on fixing it before merging.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can still be done in follow-up. Just make sure we don't move on before it is fixed 🙏🏻.

@julieg18 julieg18 marked this pull request as ready for review July 31, 2023 17:36
@julieg18 julieg18 requested a review from sroy3 as a code owner July 31, 2023 17:36
@julieg18 julieg18 marked this pull request as draft July 31, 2023 17:36
@julieg18 julieg18 marked this pull request as ready for review July 31, 2023 17:39
extension/src/cli/dvc/constants.ts Outdated Show resolved Hide resolved
extension/src/plots/model/collect.ts Outdated Show resolved Hide resolved
extension/src/plots/paths/collect.test.ts Outdated Show resolved Hide resolved
extension/src/plots/paths/collect.ts Outdated Show resolved Hide resolved
if (!stateStep) {
return 0
}
return stateStep > imgsLength - 1 ? imgsLength - 1 : stateStep
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[F] This behaviour isn't very intuitive. I had no idea what was happening when running a new experiment. I would expect the image to appear as missing if the image for that step does not exist yet. This will probably simplify the code as well

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explanatory variable for imgsLength - 1 would also be good.

Copy link
Contributor Author

@julieg18 julieg18 Aug 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behaviour isn't very intuitive. I had no idea what was happening when running a new experiment. I would expect the image to appear as missing if the image for that step does not exist yet. This will probably simplify the code as well

I chose the new behavior since I was avoiding our sliders having mutatable max length. Example:

Screen.Recording.2023-08-01.at.10.55.03.AM.mov

But if you think the behavior above is preferred though, I'm happy to change it. We also could look into finding the max length across all the plots in the backend so the sliders aren't as buggy. Can we save this change for a followup?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the slider should have the max number of available steps for all experiments but also show missing if that step is not available for that experiment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the slider should have the max number of available steps for all experiments but also show missing if that step is not available for that experiment.

Ok, so just making sure I'm understanding things correctly. Let's say we have two experiments, one with 12 images, and one with 5 images. We want the plots to render two plots, each with a slider with a range of 0-11?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we are overcomplicating and shouldn't be syncing these sliders across experiments. It sounded nice, but it might occasionally be frustrating if the user actually wants to compare different steps. We could always further optimize later. Maybe @daavoo or others have ideas here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we are overcomplicating and shouldn't be syncing these sliders across experiments. It sounded nice, but it might occasionally be frustrating if the user actually wants to compare different steps. We could always further optimize later. Maybe @daavoo or others have ideas here.

I am ok with not syncing sliders

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense! I can remove the syncing logic for now and we can try out different ideas in a followup.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the flip flopping here @julieg18!

.multiImageWrapper {
.image,
.noImageContent {
height: 380px;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really weird. Definitely need to fix.

id: column.id
id: column.id,
imgs: revs[column.id]?.imgs || [
{ errors: undefined, loading: false, url: undefined }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Q] Shouldn't this be taken care of in the backend?

Copy link
Contributor Author

@julieg18 julieg18 Aug 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a frontend test called should add a new column if there is a new revision that sends data that includes an extra revision but no plot data that matches the revision. I thought that there was a chance of this actually happening so I adjusted the code to handle it. But is the test actually incorrectly written and there is no chance of the backend sending data like that?

extension/src/plots/model/collect.ts Outdated Show resolved Hide resolved
@julieg18
Copy link
Contributor Author

julieg18 commented Aug 4, 2023

@julieg18 julieg18 merged commit 5b13368 into add-comparison-multi-image-plot Aug 4, 2023
@julieg18 julieg18 deleted the add-image-by-step-plots branch August 4, 2023 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
product PR that affects product
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants