本文介绍了类型错误:无法读取未定义的属性“分类"(保存 ml5.js 模型)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

TypeError: 无法读取未定义的属性分类"

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

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

export const 视频:React.FC=(道具:组件道具)=>{const [预测,setPrediction] = useState();让捕获:p5Types.Element;让分类器:任何;const setup = (p5: p5Types, canvasParentRef: Element) =>{捕获 = p5.createCapture(p5.VIDEO).parent(canvasParentRef);const featureExtractor = ml5.featureExtractor('MobileNet', modelReady);分类器 = featureExtractor.classification(capture, videoReady);}const draw = (p5: p5Types) =>{}函数得到结果(){分类器.分类(捕获,(错误:任何,结果:任何)=> {setPrediction(result[0].label);});}功能火车(){分类器.train((lossValue: any) => {console.log('损失是', lossValue);});//分类器.save();}return (<div><Sketch setup={setup} draw={draw} className=sketch"/><div className="按钮"><按钮变体=包含"颜色=主要"onClick={() =>classifier.addImage('first')}>First</Button><按钮变体=包含"颜色=主要"onClick={() =>classifier.addImage('second')}>Second</Button>

<div className="secondbutton"><按钮变体=包含"颜色=主要"onClick={() =>train()}>火车!</Button><按钮变体=包含"颜色=主要"onClick={() =>gotResult()}>Test!</Button><br/><span>预测:{预测}</span>

</div>);};

代码沙盒:

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

更新代码:

export const VideoComponent: React.FC=(道具:组件道具)=>{const [预测,setPrediction] = useState();const [confidence, setConfidence] = useState();const [trainingComplete, setTrainingComplete] = useState();const captureRef = useRef();constclassifierRef = useRef();const setup = (p5: p5Types, canvasParentRef: Element) =>{const capture = p5.createCapture(p5.VIDEO).parent(canvasParentRef);const featureExtractor = ml5.featureExtractor(MobileNet", modelReady);captureRef.current = 捕获;classifierRef.current = featureExtractor.classification(capture, videoReady);}const draw = (p5: p5Types) =>{}函数得到结果(){console.log('结果中的分类器',classifierRef.current);分类器Ref.current.classify(captureRef.current, (err: any, result: any) => {setPrediction(result[0].label);setConfidence(result[0].confidence);});}功能火车(){console.log('训练中的分类器',classifierRef.current);classifierRef.current?.classify.train((lossValue: any) => {console.log('损失是', lossValue);如果(lossValue == null){//setTrainingComplete(true);console.log('训练完成')}});}返回 (<div><Sketch setup={setup} draw={draw} className=sketch";/><div className="按钮"><按钮变体=包含"颜色=主要"onClick={() =>{classifierRef.current?.classifier.addImage('first');console.log('图片添加') }}>第一个</Button><按钮变体=包含"颜色=主要"onClick={() =>{classifierRef.current?.classifier.addImage('second');console.log('图片添加') }}>第二个</Button>

<div className="secondbutton"><按钮变体=包含"颜色=主要"onClick={() =>train()}>火车!</Button><按钮变体=包含"颜色=主要"onClick={() =>gotResult()}>Test!</Button><br/>{trainingComplete &&(<span>训练完成!</span>)}<br/><span>预测:{预测}</span><br/>

</div>);};

解决方案

问题在于两个let变量,captureclassifier,用于存储有状态数据.

让捕获:p5Types.Element;让分类器:任何;const setup = (p5: p5Types, canvasParentRef: Element) =>{捕获 = p5.createCapture(p5.VIDEO).parent(canvasParentRef);const featureExtractor = ml5.featureExtractor('MobileNet', modelReady);分类器 = featureExtractor.classification(capture, videoReady);}

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

您需要在代码中添加一些额外的条件检查,因为 captureRef.current 可以是 Elementundefined,因此您需要在使用它之前确保你有一个 Element.对于 classifier,您可以使用 可选链操作符像这样:classifierRef.current?.classify().

const captureRef = useRef();constclassifierRef = useRef();const setup = (p5: p5Types, canvasParentRef: Element) =>{//我在分配给 ref 之前分配给一个变量//这样我们就知道它总是在这个函数中定义的const capture = p5.createCapture(p5.VIDEO).parent(canvasParentRef);const featureExtractor = ml5.featureExtractor(MobileNet", modelReady);captureRef.current = 捕获;classifierRef.current = featureExtractor.classification(capture, videoReady);控制台日志(开始",classifierRef.current);};

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

I am using an ml5.js setup in my React app where I click on a button to train a model and then I click on another button to make predictions. The Test button works on the first time but it throws an error on the second time:

TypeError: Cannot read property 'classify' of undefined

I believe this is because once I run the prediction part, the model gets deleted? Because the classifier is now undefined. How can I modify it such that I can repeatedly click on the Test button and obtain new predictions each time.

I tried the save() function but apparently it only downloads the model and not save it for the app.

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>)
        ;
};

Codesandbox:

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

Updated code:

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>)
        ;
};
解决方案

The problem is the two let variables, capture and classifier, which you are using to store stateful data.

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);
}

These variables get re-created on every re-render. If you want to have instance variables which persist across re-renders then you need to use useState or useRef. Since these two variables come from the p5 package and might be modified by other functions I think useRef is probably what you want here. You can read more about useRef in the React docs.

You need to add some extra conditionally checks in your code since captureRef.current could be either an Element or undefined, so you need to make sure that you have an Element before using it. For classifier you can use the optional chaining operator like this: 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);
};

You should try to find or create an actual type for classifier instead of any. It looks like there is no types package for ml5, but someone created a draft.

这篇关于类型错误:无法读取未定义的属性“分类"(保存 ml5.js 模型)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-20 18:39