0

我在我的 React 应用程序中使用 ml5.js 设置,单击一个按钮来训练模型,然后单击另一个按钮进行预测。测试按钮第一次工作,但第二次抛出错误:

TypeError: Cannot read property 'classify' of undefined

我相信这是因为一旦我运行预测部分,模型就会被删除?因为分类器现在是未定义的。我该如何修改它,以便我可以反复单击测试按钮并每次都获得新的预测。

我尝试了 save() 函数,但显然它只下载模型而不为应用程序保存它。

export const Video: React.FC<ComponentProps> = (props: ComponentProps) => {
    const [prediction, setPrediction] = useState<string>();

    let capture: p5Types.Element;
    let classifier: any;
    const setup = (p5: p5Types, canvasParentRef: Element) => {
        capture = p5.createCapture(p5.VIDEO).parent(canvasParentRef);
        const featureExtractor = ml5.featureExtractor('MobileNet', modelReady);
        classifier = featureExtractor.classification(capture, videoReady);
    }

    const draw = (p5: p5Types) => {
    }
    function gotResult() {
        classifier.classify(capture, (err: any, result: any) => {
            setPrediction(result[0].label);
        });
    }

    function train() {
        classifier.train((lossValue: any) => {
            console.log('Loss is', lossValue);
        });
        //classifier.save();
    }



    return (<div><Sketch setup={setup} draw={draw} className="sketch" />
        <div className="button">
            <Button variant="contained" color="primary" onClick={() => classifier.addImage('first')}>First</Button>
            <Button variant="contained" color="primary" onClick={() => classifier.addImage('second')}>Second</Button>
        </div>
        <div className="secondbutton">
            <Button variant="contained" color="primary" onClick={() => train()}>Train!</Button>
            <Button variant="contained" color="primary" onClick={() => gotResult()}>Test!</Button>
            <br />
            <span>Prediction: {prediction}</span>
        </div>
    </div>)
        ;
};

代码沙盒:

https://codesandbox.io/s/hardcore-solomon-zb34l?file=/src/Component.tsx

更新代码:

export const VideoComponent: React.FC<ComponentProps> = (props: ComponentProps) => {
    const [prediction, setPrediction] = useState<string>();
    const [confidence, setConfidence] = useState<string>();
    const [trainingComplete, setTrainingComplete] = useState<boolean>();
    const captureRef = useRef<p5Types.Element>();
const classifierRef = useRef<any>();

    const setup = (p5: p5Types, canvasParentRef: Element) => {

        const capture = p5.createCapture(p5.VIDEO).parent(canvasParentRef);
    const featureExtractor = ml5.featureExtractor("MobileNet", modelReady);
    captureRef.current = capture;
    classifierRef.current = featureExtractor.classification(capture, videoReady);
    }

    const draw = (p5: p5Types) => {
    }


    function gotResult() {
        console.log('classifier in results', classifierRef.current);
        classifierRef.current.classify(captureRef.current, (err: any, result: any) => {
            setPrediction(result[0].label);
            setConfidence(result[0].confidence);

        });
    }

    function train() {
        console.log('classifier in train', classifierRef.current);
        classifierRef.current?.classify.train((lossValue: any) => {
            console.log('Loss is', lossValue);
            if (lossValue == null) {
                //setTrainingComplete(true);
                console.log('training complete')
            }
        });
    }



    return (
    <div>
        <Sketch setup={setup} draw={draw} className="sketch" />
        <div className="button">
            <Button variant="contained" color="primary" onClick={() => { classifierRef.current?.classifier.addImage('first'); console.log('image added') }}>First</Button>
            <Button variant="contained" color="primary" onClick={() => { classifierRef.current?.classifier.addImage('second'); console.log('image added') }}>Second</Button>
        </div>
        <div className="secondbutton">
            <Button variant="contained" color="primary" onClick={() => train()}>Train!</Button>
            <Button variant="contained" color="primary" onClick={() => gotResult()}>Test!</Button>
            <br />
            {trainingComplete && (<span>Training Complete!</span>)}<br />
            <span>Prediction: {prediction}</span><br />
        </div>
    </div>)
        ;
};
4

1 回答 1

0

问题在于您用于存储有状态数据的两个let变量capture和。classifier

let capture: p5Types.Element;
let classifier: any;
const setup = (p5: p5Types, canvasParentRef: Element) => {
    capture = p5.createCapture(p5.VIDEO).parent(canvasParentRef);
    const featureExtractor = ml5.featureExtractor('MobileNet', modelReady);
    classifier = featureExtractor.classification(capture, videoReady);
}

这些变量会在每次重新渲染时重新创建。如果您希望实例变量在重新渲染中持续存在,那么您需要使用useStateor useRef。由于这两个变量来自p5包并且可能被其他函数修改,我认为useRef这可能是你想要的。您可以在 React 文档中阅读更多信息useRef

您需要在代码中添加一些额外的条件检查,因为captureRef.current可能是 anElementundefined,因此您需要确保Element在使用它之前有 an 。因为classifier您可以像这样使用可选的链接运算符classifierRef.current?.classify().

const captureRef = useRef<p5Types.Element>();
const classifierRef = useRef<any>();

const setup = (p5: p5Types, canvasParentRef: Element) => {
    // I'm assigning to a variable before assigning to the ref
    // so that we know that it is always defined inside this function
    const capture = p5.createCapture(p5.VIDEO).parent(canvasParentRef);
    const featureExtractor = ml5.featureExtractor("MobileNet", modelReady);
    captureRef.current = capture;
    classifierRef.current = featureExtractor.classification(capture, videoReady);
    console.log("start", classifierRef.current);
};

您应该尝试查找或创建一个实际类型,classifier而不是any. 看起来没有 types 包ml5,但有人创建了一个草稿

于 2021-04-19T01:17:09.900 回答