React05:useEffect 与副作用

本文深入解析 React useEffect 的核心机制,涵盖其作为 onMounted/watch/onUnmounted 三合一的心智模型、依赖数组的三种写法及闭包陷阱、cleanup 函数的双重执行时机(依赖变更前与组件卸载时)、请求数据的标准三态+竞态处理模式,以及 StrictMode 下双执行的原理与应对。强调企业级最佳实践:诚实填写依赖、必写清理逻辑、避免 effect 地狱,并指出何时不该使用 useEffect。

第 05 课 · useEffect 与副作用

Vue 里「组件挂载后做事」「监听数据变化」「卸载前收尾」分属 onMounted / watch / onUnmounted 三个 API;React 把它们合成一个 useEffect。这是两个框架心智差异最大的地方,也是 Vue 开发者最常写出 bug 的地方——本课一半篇幅都在纠偏。

🎯 本课目标

  • 建立「useEffect = onMounted + watch + onUnmounted 三合一」的心智模型,分清它和 watch 的关键差异
  • 熟练使用依赖数组三形态(不写 / [] / [a, b]),能预判每种写法在「任何状态变化」下的行为
  • 给定时器、事件监听、订阅写清理函数,说清 cleanup 的执行时机
  • 写出企业标准的请求数据模式:loading/error/data 三态 + ignore 标志处理竞态
  • 用官方「You Might Not Need an Effect」的判断标准,识别 code review 里该删的 useEffect

1. 副作用与「三合一」心智

Vue 里你这样写

import { onMounted, onUnmounted, watch, ref } from 'vue'
const keyword = ref('')

onMounted(() => { document.title = '搜索页' })        // 挂载后做事
watch(keyword, kw => { console.log('搜索:', kw) })   // 数据变化后做事
onUnmounted(() => { window.removeEventListener('resize', onResize) }) // 卸载前收尾

React 里这样写

import { useEffect, useState } from 'react'

export default function SearchPage() {
  const [keyword, setKeyword] = useState('')

  // ① 挂载后做事(= onMounted)
  useEffect(() => {
    document.title = '搜索页'
  }, [])

  // ② 数据变化后做事(= watch(keyword, ...))
  useEffect(() => {
    console.log('搜索:', keyword)
  }, [keyword])

  // ③ 卸载前收尾(= onUnmounted,写在 return 里)
  useEffect(() => {
    const onResize = () => console.log('resize')
    window.addEventListener('resize', onResize)
    return () => window.removeEventListener('resize', onResize)
  }, [])

  return <input value={keyword} onChange={e => setKeyword(e.target.value)} />
}

关键差异与注意点

  • 副作用:渲染之外的事——请求数据、操作 DOM、定时器、订阅、打点。React 里「渲染必须是纯的」,这些事统一交给 useEffect。
  • 执行时机:每次渲染提交到屏幕之后异步执行(Vue 的 watch 默认是 flush: 'pre',在组件更新前)。所以 effect 里再改 state 不会卡住首屏绘制。
  • 一个组件可以有任意多个 useEffect,按「这件事」拆分,而不是像 Vue 那样按「生命周期」聚堆。
  • effect 的两个参数:(执行函数, 依赖数组)。依赖数组决定「什么时候再执行」,return 的函数决定「执行前怎么收尾」。

2. 依赖数组三形态

Vue 里你这样写

watchEffect(() => {})                 // 自动收集依赖
watch([a, b], () => {})               // 显式指定依赖
onMounted(() => {})                   // 只跑一次

React 里这样写

依赖数组没有自动收集,必须手写、写全:

// 形态一:不写第二个参数 —— 每次渲染后都执行(几乎不用,用了多半是 bug)
useEffect(() => {
  console.log('任何 state 变化、任何父组件重渲染导致的重渲染,我都会跑')
})

// 形态二:空数组 —— 仅首次挂载后执行(= onMounted)
useEffect(() => {
  fetchConfig()
}, [])

// 形态三:[a, b] —— 首次执行 + a 或 b 变化后执行(= onMounted + watch(a/b))
useEffect(() => {
  console.log('a 或 b 变了', a, b)
}, [a, b])

关键差异与注意点

  • 与 watch 的关键差异(最容易理解偏差的地方):watch 默认不立即执行,只有依赖变化才跑;useEffect 挂载后必执行一次,然后才按依赖数组决定是否重跑。所以 useEffect(fn, [a]) ≈ watch(a, fn, { immediate: true }),而不是普通 watch。
  • 依赖数组比较用的是 Object.is(浅比较)。对象/数组依赖是重灾区:useEffect(..., [form]) 里 form 是每次渲染新建的对象时,effect 每次渲染都会跑。要么放具体字段 [form.keyword],要么记忆化(第 7 课 useMemo)。
  • 诚实填写:effect 里用到的响应式值(state、props、以及组件内定义的函数)都要进依赖。漏写不会报错,但会读到「上一次渲染的旧值」——这类 bug 比报错难查得多。关掉 exhaustive-deps 的 lint 规则来逃避填依赖,是企业 code review 一票否决项。
  • 想要「仅依赖变化时执行、跳过首次」?用 ref 记录是否首次,不要 hack 依赖数组。

3. 清理函数:return 出去的收尾逻辑

Vue 里你这样写

onMounted(() => { window.addEventListener('resize', onResize) })
onUnmounted(() => { window.removeEventListener('resize', onResize) }) // 收尾写在另一个 API 里

React 里这样写

import { useEffect, useState } from 'react'

export default function Clock() {
  const [now, setNow] = useState(() => new Date())

  useEffect(() => {
    const timer = setInterval(() => setNow(new Date()), 1000)
    const onResize = () => console.log('resize')

    window.addEventListener('resize', onResize)
    // cleanup:下一次 effect 执行前 + 组件卸载时,各调用一次
    return () => {
      clearInterval(timer)
      window.removeEventListener('resize', onResize)
    }
  }, [])

  return <p>{now.toLocaleTimeString()}</p>
}

关键差异与注意点

  • 执行时机:不是只在卸载时!cleanup 在下一次 effect 执行之前也会执行(依赖变化 → 先清理上一轮,再跑新一轮),以及组件卸载时。这一点比 onUnmounted 覆盖的场景多——竞态处理(第 4 节)全靠它。
  • cleanup 必写清单:setInterval/setTimeout(clearInterval/clearTimeout)、addEventListener(removeEventListener)、WebSocket/EventSource、订阅(store、PubSub)、手动修改的 DOM/第三方实例(destroy/disconnect)。
  • 不写 cleanup 的后果:组件卸载后定时器还在跑、监听还在触发——内存泄漏 + 日志错乱 + 对已卸载组件 setState。
  • effect 里只能 return 函数,所以 async 函数不能直接写在 effect 里(见第 4 节的正确姿势)。
  • useEffect 的 cleanup 里 setState 是允许的(React 18+ 已移除卸载后 setState 的警告),但「清理」本身不该做业务动作(比如退出登录),那是别处的事。

4. 请求数据标准模式:三态 + 竞态

Vue 里你这样写

const loading = ref(false)
const error = ref('')
const user = ref(null)

async function loadUser(id: number) {
  loading.value = true
  error.value = ''
  try {
    user.value = await fetchUser(id)
  } catch (e) {
    error.value = String(e)
  } finally {
    loading.value = false
  }
}
watch(() => props.id, id => loadUser(id), { immediate: true })

React 里这样写

请求随 id 变化自动重发,ignore 标志保证「慢的旧响应」不会覆盖新结果:

import { useEffect, useState } from 'react'

interface User { id: number; name: string; role: string }

export default function UserProfile({ id }: { id: number }) {
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState('')
  const [user, setUser] = useState<User | null>(null)

  useEffect(() => {
    let ignore = false // 本轮 effect 的「过期开关」

    async function load() {
      setLoading(true)
      setError('')
      try {
        const data = await fetchUser(id)
        if (!ignore) setUser(data)          // 已过期就丢弃
      } catch (e) {
        if (!ignore) setError((e as Error).message)
      } finally {
        if (!ignore) setLoading(false)
      }
    }
    load()

    return () => { ignore = true } // 下一次 effect 前 / 卸载时执行
  }, [id])

  if (loading) return <p>加载中…</p>
  if (error) return <p style={{ color: 'red' }}>{error}</p>
  if (!user) return null
  return <p>{user.name}({user.role})</p>
}

关键差异与注意点

  • 三态拆开存:loading / error / data 三个 state(或合成一个带类型的对象),UI 按优先级渲染:loading → error → data。
  • 竞态:请求是异步的,切到用户 2 后用户 1 的慢响应可能后到,把新数据覆盖掉。let ignore = false + cleanup 里 ignore = true 是官方推荐的最小方案;也可以用 AbortController 真·取消请求(fetchUser 内部把 signal 传给 fetch)。
  • async 不能直接写在 effect 里:useEffect(async () => {}) 会报错——effect 必须返回函数或什么都不返回。标准做法是 effect 里定义 async function load() 然后 load()。
  • 初次挂载就能拿到数据(不需要 immediate: true),因为 useEffect(fn, [id]) 挂载时必执行一次——这正好符合列表页「进页面就请求」的习惯。
  • 企业现状:手写这套模式只用于理解原理,实际项目用 TanStack Query(本课程第 11 课),缓存、重试、竞态、失效全帮你做好了。面试要能手写,上班要用库。

5. StrictMode:开发环境为什么执行两次

React 里这样写

// main.tsx —— 企业项目都会包一层 StrictMode,不要删
createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

关键差异与注意点

  • 开发环境挂载时,React 会故意执行两遍:组件函数渲染两次、effect 执行 → cleanup → 再执行一次。生产环境不会。
  • 目的:模拟「组件随时可能被卸载重挂」,逼你把 cleanup 写对。
  • 正确反应是补 cleanup,而不是删 StrictMode「修复」——删掉它等于关掉烟雾报警器,泄漏会留到生产。
  • Vue 没有这一层,所以第一次看到「请求发了两次」会很惊讶:先检查是不是 StrictMode,再查依赖数组。

6. 什么时候不该用 useEffect(企业 code review 高频意见)

Vue 里你这样写

const full = computed(() => `${first.value} ${last.value}`) // 派生数据用 computed

React 里这样写

// ❌ 多余的 effect:为「渲染时能算出来的东西」专门跑一次副作用
const [full, setFull] = useState('')
useEffect(() => {
  setFull(`${first} ${last}`)
}, [first, last])

// ✅ 直接算:渲染就是一次「用最新 state 重算 UI」的过程
const full = `${first} ${last}`

关键差异与注意点

  • 判断口诀(官方 You Might Not Need an Effect 精华):
    1. 这份值能由现有 state/props 直接算出来?→ 渲染时直接写表达式,别进 effect(Vue 里你本来就会用 computed,同理)。
    2. 这件事是某个具体交互引起的?→ 写在那个事件回调里(提交表单时发请求,而不是「监听到表单变了就发」),别进 effect。
    3. 是多处状态变化都要触发、且来源不止用户操作?→ 这才是 effect(同步第三方、埋点、操作 DOM 标题)。
  • effect 里 setState 去存「另一个 state 的派生值」会导致多一轮渲染,并且容易写出链式 effect(A 的 effect 改 B,B 的 effect 改 C)——review 里叫「effect 地狱」。
  • 事件回调能做就别进 effect(Demo 4 左右对照):事件回调只响应真实交互,没有挂载那次多余执行,逻辑也更内聚。
  • 真正适合 effect 的:请求/订阅/定时器、document.title 这类 DOM 副作用、和 React 外部系统同步。React 内部的数据加工,一律不用 effect。

📊 Vue ↔ React 对照速查表

Vue 3 写法React 写法一句话差异
onMounted(fn)useEffect(fn, [])语义一致,写法换个位置
onUnmounted(fn)useEffect(() => fn, []) 的 return收尾逻辑写在同一个 effect 里
watch(a, fn)useEffect(fn, [a])useEffect 挂载后必执行一次,watch 默认不会
watch(a, fn, { immediate: true })useEffect(fn, [a])这才是完全等价物
watch([a, b], fn)useEffect(fn, [a, b])依赖手写,不会自动收集
watchEffect(fn)useEffect(fn)(不写依赖)都容易过度执行,慎用
computed渲染中直接算 / useMemo派生数据不要进 effect
事件回调里发请求事件回调里发请求一样!能回调就别进 effect
watch 请求 + 手动防竞态ignore 标志 / AbortControllercleanup 让竞态处理有落点
onBeforeUnmount 清定时器cleanup 里 clearInterval同一件事,换个位置写

🏢 企业实战要点

  1. 依赖数组诚实填写,配合 eslint-plugin-react-hooks 的 exhaustive-deps 规则;「为了少跑几次而故意漏依赖」是 review 一票否决项,正确做法是 useMemo/useCallback 稳定依赖(第 7 课)。
  2. 对象/数组依赖的引用陷阱:[form]、[list]、[options] 这类写法,只要上游每次渲染新建对象就会次次触发。放原始值字段([form.keyword])或记忆化,review 里见到直接打回。
  3. cleanup 必写清单贴在脑子里:定时器、事件监听、订阅、WebSocket、第三方实例 destroy。PR 描述里能一句话说清「这个 effect 的清理是什么」。
  4. 请求逻辑封装成自定义 Hook(第 8 课 useUser(id) / 第 11 课 TanStack Query),组件里只留「用」,别让每个组件都手写三态 + ignore。
  5. 一个 effect 只做一件事:需要多个副作用就写多个 useEffect,依赖数组各自精确;一个大 effect 揉五个职责是「effect 地狱」的起点。
  6. onChange 里能做的不进 effect(上一课的防抖、这一课的联动同理):review 里最常见的多余 effect 就是「监听 input 值去更新另一个 state」。
  7. 别删 StrictMode,也别为了「请求只发一次」去改全局配置;用 cleanup + 正确依赖解决。
  8. effect 里不要做重计算:它跑在渲染之后,卡的是用户可感知的交互;重活挪到事件回调或用 useMemo 记忆化。

💼 面试高频

1. useEffect 的依赖数组是怎么工作的?漏写会怎样?
React 在每次渲染后用 Object.is 逐项比较新旧依赖:全相等就跳过 effect,否则先执行上一次的 cleanup 再跑新 effect。漏写依赖不会报错,但 effect 闭包会引用旧的 state/props(闭包陷阱),表现为「数据明明变了逻辑却用旧值」,是最难排查的一类 bug。所以依赖要诚实写全。

2. useEffect 和 Vue 的 watch 有什么区别?
最大差异是执行时机:watch 默认不立即执行,依赖变化才跑;useEffect 挂载后必执行一次,之后依赖变化才重跑,等价于 watch(..., { immediate: true })。另外 watch 靠响应式系统自动追踪依赖,useEffect 必须手写依赖数组且比较是浅比较(对象引用变了就算变了)。

3. cleanup 函数的意义和执行时机?
用于撤销本次副作用:清定时器、移除监听、取消订阅/请求。执行时机有两个:下一次 effect 执行之前(依赖变化时先清理上一轮)和组件卸载时。它不只是「卸载收尾」,更是处理竞态(ignore 标志)、避免重复订阅的关键机制。

4. 开发环境 useEffect 为什么执行两次?
因为 React 18+ 的 StrictMode 会在开发环境对每个组件模拟一次「挂载 → 卸载 → 重挂载」(渲染两次、effect 执行 → cleanup → 再执行),用来暴露缺失 cleanup 的副作用。生产环境只执行一次。正确做法是补全 cleanup,而不是移除 StrictMode。

5. 请求的竞态问题怎么处理?
典型场景:快速切换 id,先发出的慢响应后到,覆盖了新结果。最小方案是 effect 里 let ignore = false,响应回来先判断 if (ignore) return,cleanup 里 ignore = true 把上一轮作废;更彻底用 AbortController 真正中断请求。生产上直接用 TanStack Query,它内置 keyed 缓存与过期丢弃。

📌 小结 + 下节预告

这一课的核心是心智转换:useEffect 把 Vue 的 onMounted / watch / onUnmounted 合成一个函数,挂载必执行一次是与 watch 最关键的区别;依赖数组要诚实手写、对象依赖小心引用陷阱;定时器/监听/订阅必须 cleanup;请求数据 = 三态 + ignore 竞态处理;最后用「能在渲染里算、能在事件回调里做的,都别进 effect」来给代码瘦身。

不过你可能已经注意到 Demo 2 里 appendLog 需要包一层 useCallback、依赖数组里不敢放对象——这些「函数身份」和「引用稳定性」问题,正是下一课的主题:组件通信(props 回调、Context、useImperativeHandle),以及它背后引出的性能优化三件套。

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值